> ## Documentation Index
> Fetch the complete documentation index at: https://pipedream.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> What changed in each Conduit release, newest first, and what to do when upgrading.

<Update label="Unreleased" description="Changes on main that are not yet part of a tagged release.">
  ## Upgrade notes

  * Automation that changes a connector's URL, an OAuth client's client ID or token endpoint, an identity provider's client ID, issuer or token URL, or the SMTP host or port must now send the secret again in the same request; an update that leaves it blank while a secret is stored is refused with `invalid_argument` (see Security below).
  * On a deployment with more than one replica, finish rolling out this version before adding or editing an identity provider or OAuth connector: an older replica doesn't serve the new sign-in, connector callback and connect-page addresses, and sends the shared callback for providers and connectors that have their own. For the same reason, a provider or connector added or changed on this version doesn't sign in or connect after rolling back to an older one.
  * The `oauth_redirect_uri` that `ListIdentityProviders` and `ListConnectors` return is now only the callback that providers and connectors added before this version are registered with. Register each provider's `redirect_uri` or each connector's `oauth_redirect_uri` instead.
  * The Conduit CLI's local-connector endpoints (`AuthorizeLocalTool`, `ReportLocalEvent`, `ListEnabledLocalConnectors`) now refuse every OAuth client other than the Conduit CLI.

  ## Security

  * **A workspace's identity provider could take over accounts that sign in through another provider.** Every provider shared one sign-in redirect URI, and the callback trusted the sign-in state to say which provider a response came from. An identity provider run by a workspace admin could start a victim's sign-in, send them on to an honest provider (Google, or another workspace's provider), and receive the honest provider's code for their account — an IdP mix-up — which it could use to sign in as them, instance admins included. Each provider now has a redirect URI of its own (`/api/auth/callback/<key>`, shown on the provider screen), a response is accepted only on the URI of the provider its sign-in started with, and a response that names its issuer must name that provider.
  * **A connector's authorization server could capture a member's grant for another connector.** Every connector shared one OAuth callback URL, so a connector whose authorization server a workspace admin runs could pass a member's connect flow on to another connector's authorization server and receive the resulting code. Each connector now has a callback URL of its own (`/upstream/callback/<key>`, shown on the connector's form), a connect response is accepted only on the URL of the connector it was started for, and one that names a different authorization server than the connector's is refused.
  * **Outbound requests could reach internal IPv4 addresses through IPv6 translation.** A connector, identity provider or telemetry endpoint whose hostname resolved to a NAT64 or 6to4 address (such as `64:ff9b::a9fe:a9fe`) passed the internal-address check, and on a network with NAT64 — common on IPv6 cloud subnets — reached the IPv4 address inside it, including the cloud metadata service. Conduit now checks the IPv4 address such an IPv6 address carries, and refuses the deprecated and local-use IPv4-embedding ranges outright.
  * **A workspace identity provider's discovery document could run script on the Conduit origin.** The authorization endpoint a provider's issuer published was used as the sign-in redirect without being checked, so an issuer run by a workspace admin could answer with a `javascript:` URL that anyone opening that workspace's login page would execute (the default content security policy blocked it; `CONDUIT_CSP=report-only` or `off` did not). Discovered authorization and token endpoints are now held to the same rule as ones an admin types in — `https`, not an internal address — and a provider that publishes anything else fails sign-in with "Couldn't reach the identity provider".
  * **A workspace's MCP client rules could be bypassed from another workspace.** "Who can connect", per-app rules and client revocations held only at the workspace's own MCP URL, so a client it refused could still use workspace tools against it through a connection to another workspace, and any MCP client — not just the Conduit CLI — could fetch the member's local (stdio) connector definitions, environment secrets included. The rules now apply wherever a client acts in the workspace (and such a client shows up on its MCP Clients page), and only the Conduit CLI can call the local-connector endpoints.
  * **A connect link could connect your account to a different workspace.** The link an assistant gives you to connect an account (an OAuth sign-in, an API token, or a Pipedream app) used whichever workspace your browser was last switched to — which another site could change — rather than the workspace the assistant was using. A member of more than one workspace could have their account land in another workspace's connector or Pipedream project. Connect links now name their workspace, every connect step acts in that workspace, and the connect page shows which workspace you're connecting to. A link for a workspace you aren't a member of connects nothing.
  * **An admin could redirect a stored secret they can't read.** Changing where a secret is sent — a connector's URL, an OAuth client's client ID or token endpoint, an identity provider's client ID, issuer or token URL, or the SMTP host or port — kept the stored secret when its field was left blank, so a co-admin or an API client with `connectors:write` could have Conduit send it to a host they control; these edits now require the secret to be re-entered. An instance telemetry exporter moved to a new endpoint or protocol now drops its stored headers, as a workspace exporter already did. Repointing a connector also discovers an MCP connector's OAuth provider again for the new URL and cancels connect flows already in progress.

  ## Changed

  * The workspace menu shows **Switch Workspace** only when you belong to another workspace, and **Create Workspace** only when you're allowed to create one.
  * The login, invite and join pages use full-width buttons, and provider buttons read **Continue with** the provider.
  * On a browser that hasn't signed in before, the login page says **Sign up to continue** when it offers a sign-in provider, since a provider's first sign-in creates the account.

  ## Fixed

  * Dialogs in the web UI are back to a comfortable width, consistent across the app, and a dialog taller than the window scrolls instead of running its buttons off the bottom of the screen.
</Update>

<Update label="v0.14.0" description="2026-10-01">
  *The docs move to Mintlify and gain a cloud edition, API client settings are redesigned, and the CONDUIT\_DOCS\_URL default changes.*

  ## Upgrade notes

  * The default `CONDUIT_DOCS_URL` is now `https://pipedream.com/docs/conduit`, and in redirect mode `/docs/` no longer adds a version to the path. If you relied on the old default, `https://conduit.pipedream.com/docs`, set `CONDUIT_DOCS_URL` explicitly.

  ## Added

  * `CONDUIT_DOCS_EDITION` selects which edition of the embedded docs `/docs/` serves: `self-hosted` (the default, the full docs) or `cloud`.

  ## Changed

  * The docs are rendered with Mintlify and published at `pipedream.com/docs/conduit`.
  * The image is about 70 MB larger.
  * Docs URLs no longer end in a slash. An old URL with a trailing slash redirects to the new one.
  * The changelog is one page, with an anchor for each release (for example, `/docs/changelog#v0-13-4`). Old per-release changelog URLs, `/docs/deploy/load-tests` and the old API operation pages redirect to their new location.
  * **Settings → API Clients** lists clients in a table and manages each client's scopes and credentials from its row menu. A client's credentials dialog shows its client ID and token endpoint together, and revoking a credential asks for confirmation first.
  * A newly created client secret or SCIM token is hidden until you choose to show it, and can be copied without revealing it.
  * The Admin API is now called the API in the web app and the docs, and its pages moved to `/docs/use/api` and `/docs/use/api-reference`. The old addresses redirect to the new ones.

  [Compare v0.13.4...v0.14.0](https://github.com/PipedreamHQ/conduit/compare/v0.13.4...v0.14.0)
</Update>

<Update label="v0.13.4" description="2026-09-30">
  *Instance-wide help instructions, display names from your identity provider and editable by members and admins, workspace slugs removed, and OAuth endpoint error fixes.*

  ## Upgrade notes

  * Workspaces no longer have a slug. The `slug` fields are gone from the Admin API (`UpdateWorkspace`) and from every workspace in RPC and MCP tool responses. An RPC client that still sends one has it ignored; an MCP tool call that passes one is refused as an unknown argument. No migration runs, so upgrading and rolling back are unaffected.
  * Help instructions are now set once for the whole instance, not per workspace. A database migration, run automatically on boot, keeps the existing text when the instance has exactly one workspace (every single-workspace instance). An instance with more than one workspace starts with none, even if only one workspace had instructions, so one workspace's text is never shown to everyone: an instance admin sets them again from **?** → **Edit**. Help edited while an upgrade is rolling out, or before a rollback, may be lost. `GetSession` reports `helpSet` in place of `activeWorkspaceHelpSet`, in RPC and MCP tool responses.

  ## Changed

  * Only instance admins can edit the help instructions (**?** → **Edit**), and every signed-in user sees the same instructions, in every workspace or none. Workspace owners and admins who aren't instance admins can no longer edit them.
  * A new account's display name comes from the identity provider's `name` claim, or its `given_name` and `family_name` claims when `name` is absent. With none of them, or when accepting an invite without a name, the name is the part of the email address before the `@`, as before, and a later SSO sign-in that asserts a name replaces it. Names set in Conduit are never replaced. A database migration, run automatically on boot, records where each name came from; existing names are treated as set in Conduit.
  * Anyone can change their own name from the account menu (**Edit profile**), or with the `UpdateMyProfile` RPC, except someone SCIM provisions: their directory owns their name.
  * Workspace admins can rename members in **Settings → Members**, or with the `UpdateWorkspaceMemberName` RPC and its builtin MCP tool, when the member signs in through one of the workspace's own identity providers, isn't provisioned by SCIM, and doesn't outrank them. The members list shows why a rename isn't available, and `ListWorkspaceMembers` reports it per member (`name_edit`; unset for API clients, which can't rename). The member actions now sit in a menu next to **Review access**.
  * A personal workspace, created for a new account at its first SSO sign-in in multi-workspace mode, is now named "Personal".
  * Workspaces are identified by name alone in the web app: the slug field is gone from workspace settings, **Create Workspace** and the instance admin's workspace editor. **Settings → Workspaces** (instance admin) shows each workspace's owner under its name, and its search matches owner emails as well as names.
  * The unauthenticated `GetSetupStatus` RPC is gone; `GetAuthMethods` reports whether first-run setup is still needed (`needsSetup`).
  * Refreshed the web UI's components: confirmation dialogs can't be dismissed by a backdrop click or while their action is running and open as a bottom sheet on small screens, the audit log's filters form a single toolbar, and IDs in an audit event's details copy with a click.

  ## Fixed

  * OAuth error responses from the token, device authorization and client registration endpoints carry `Cache-Control: no-store`, as client-credentials errors already did. An internal failure at these endpoints is a 500 `server_error` rather than a 400; a database failure while looking up a device code or the user behind a grant was reported as `invalid_grant`. A client registration refused because the instance has reached its registered-client limit is a 503 `temporarily_unavailable` rather than a 400 `invalid_client_metadata`.

  [Compare v0.13.3...v0.13.4](https://github.com/PipedreamHQ/conduit/compare/v0.13.3...v0.13.4)
</Update>

<Update label="v0.13.3" description="2026-09-29">
  *Tidier traces: API and SQL spans are renamed for easier monitoring, background jobs and auth checks nest inside the requests that cause them, and first-run setup is atomic.*

  ## Upgrade notes

  * API request spans are named for the RPC — `conduit.v1.ConduitService/GetUserUsage` rather than `GET /conduit.v1.ConduitService/GetUserUsage` — with the bare method (`GetUserUsage`) as their `resource.name`. APM monitors, dashboards or saved views that match the old span or resource names need updating; `http.route` and the `http_server_request_duration_seconds` metric's `http_route` label are unchanged.
  * Database statement spans from generated queries are named with a `sql.` prefix — `sql.GetUser` rather than `GetUser` — so they are easy to tell apart from API request spans. Monitors or dashboards matching the old names need updating.

  ## Changed

  * API request spans carry `rpc.system` (`connect_rpc`), `rpc.service` and `rpc.method` on every call, not only on failures.
  * The instance liveness heartbeat, written every 30 seconds, no longer exports a trace for each of its database statements to instance telemetry exporters.
  * The session check that runs on every signed-in request now appears inside that request's trace, instead of exporting as a separate one-span trace per request.
  * Opening a new Postgres connection, including building an RDS IAM token, is its own `postgresql.connect` span in the trace of the request that needed it, instead of counting toward that request's first query.
  * With Postgres, an unused database connection now stays open for 30 minutes (previously 5) before Conduit closes it, so the first page load after a quiet stretch no longer waits on new connections.
  * Issuing an OAuth access token (authorization code, refresh, device and client-credentials grants) records its database work inside the token request's trace, instead of as several separate one-span traces per token.
  * With Postgres, counting a failed sign-in, MCP, SCIM or API-client authentication attempt toward its cluster-wide throttle is recorded inside that request's trace, and the counters' periodic cleanup no longer exports a trace each time it runs.
  * When an access or connector change notifies connected MCP clients, each recheck of their access is one `mcp.notify.access_changed` or `mcp.notify.lists_changed` trace, linked to the change that caused it, instead of separate one-span traces for every connected member's database reads. Log-level lookups for forwarded connector log messages, and recording which MCP clients reach a workspace, are traced inside the request that caused them.
  * The hourly sweep of expired OAuth codes, tokens and sessions is exported as one `oauth.cleanup` trace with its database statements beneath it, instead of a separate trace for each statement.
  * With several replicas on Postgres, what each other replica does in response to a change (clearing caches, notifying its connected MCP clients) appears in the trace of the request that made the change, under a `cluster.dispatch` span. The cache rebuild after a replica's event listener reconnects is one `cluster.resync` trace.
  * Completing first-run setup creates the admin account, its password and the default workspace in one transaction, so an interrupted setup leaves nothing behind and can be retried. Setup, password rotation and break-glass sign-in record their database work inside the request's trace.

  [Compare v0.13.2...v0.13.3](https://github.com/PipedreamHQ/conduit/compare/v0.13.2...v0.13.3)
</Update>

<Update label="v0.13.2" description="2026-09-28">
  *Email sender settings save on their own, gRPC exporters with https endpoints use TLS for logs and metrics, and signing in returns you to the page you opened.*

  ## Upgrade notes

  In a multi-replica deployment, saving only the email sender through a replica
  still on the previous version turns email off. After the rollout, check
  **Settings → Instance → Email** and save the provider again if it's off.

  ## Changed

  * The email settings page shows the from address and name once, above the providers, instead of in every provider's section, and they can be saved on their own with **Save sender**.
  * Updating email settings through the `UpdateEmailConfig` RPC or its builtin MCP tool keeps the active provider when a request omits `provider`, so a request can change just the sender or a provider's settings; `enabled: false` turns email off, and a request that names a provider alongside it, sets `enabled: true` with no provider, names an unknown provider, or gives a from address that isn't one bare address (with email off too) is refused. The `enabled` a response reports always agrees with its provider.
  * Instance telemetry settings refuse an exporter endpoint that isn't an `http://` or `https://` URL with a host, since the scheme is what picks plaintext or TLS.

  ## Fixed

  * Logs and metrics sent to an instance gRPC exporter with an `https` endpoint now use TLS instead of plaintext, which the collector refused.
  * Opening a Conduit page while signed out, such as a connect link from an MCP client, returns to that page after signing in instead of the home page.

  [Compare v0.13.1...v0.13.2](https://github.com/PipedreamHQ/conduit/compare/v0.13.1...v0.13.2)
</Update>

<Update label="v0.13.1" description="2026-09-28">
  *Email settings are validated so they can't be saved in a state that fails to send, multi-replica deployments deliver config changes, cancellations and prompt answers reliably, and concurrent first SSO sign-ins no longer fail.*

  ## Upgrade notes

  None.

  ## Changed

  * Email settings have their own entry in the admin sidebar and a [docs page](/docs/conduit/configure/email).
  * Each email provider's settings are shown inline in their own section, with the active one outlined and marked **Active**, so another provider can be checked or filled in while the active one keeps sending, and saving that provider's form switches all email to it.
  * Turning email off asks for confirmation and keeps every provider's saved settings, so switching back doesn't mean entering them again.
  * The Amazon SES region is chosen from a list of SES regions, or typed in as a region code under **Other region…**.
  * Recording usage no longer sends your telemetry backend a separate one-span trace for each statement it writes (at least one per MCP tool call, prompt or resource read), and each usage rollup or retention pass is now one trace with its statements beneath it instead of a trace per statement. A rollup check that finds nothing new sends no trace, and a failed rollup or retention pass is reported to error tracking as a `conduit.usage.maintenance_failed` issue.
  * MCP tool listings and Pipedream tool calls no longer read and decrypt the workspace's Pipedream settings on every request, including in workspaces that never set Pipedream up.

  ## Fixed

  * Email settings that can't send are now refused instead of saving and then failing every send (invites included), including an edit to saved settings that already can't send: a from address that isn't one bare address, SMTP without a host or a port from 1 to 65535, Resend without an API key, or SES without a valid region code or access key.
  * The email settings page now warns when the active provider's saved settings are missing something they need to send, such as the SES region whose absence made sends fail with a DNS error for `email..amazonaws.com`.
  * Saving email settings while the stored settings can't be read now fails with an error instead of clearing every field the save didn't include, saved SMTP, Resend and SES credentials among them.
  * Two first sign-ins through the same identity provider at the same moment (for example, in two tabs) now both land on one account, instead of the second failing with an error and leaving a stray, unusable user behind.
  * In multi-replica deployments, a change saved just as its client disconnected is no longer dropped before reaching the other replicas, which then kept serving stale connector lists and telemetry exporters until the next change, and stale access for up to 30 seconds.
  * In multi-replica deployments, an MCP cancellation now reaches the replica running the request, and a prompt answer reaches it at once rather than up to 15 seconds late, even when the client exits right after sending it.
  * In multi-replica deployments, saves no longer stall when a replica's event connection to Postgres hangs; each waits at most 5 seconds for it.
  * Finishing a connector sign-in now completes the MCP client's pending prompt even when the browser leaves the page right away.
  * On Postgres, the audit log lists events recorded in the same second newest first, in one fixed order, so paging through a log that isn't changing no longer shows them out of order or repeats or skips them.
  * The MCP endpoint and the API now accept a bearer token whose scheme is written in any case (the token endpoints answer `token_type: "bearer"`, which some clients echo into their `Authorization` header) or that has extra spaces before it, as SCIM already did, instead of refusing it as unauthenticated.
  * A member's Pipedream connection no longer keeps using a workspace's old Pipedream settings after they change while the member's request is in flight, and in multi-replica deployments a replica that missed the change picks it up within 30 seconds.
  * Log records from background work that isn't traced no longer carry a trace id that no trace in your backend matches.

  [Compare v0.13.0...v0.13.1](https://github.com/PipedreamHQ/conduit/compare/v0.13.0...v0.13.1)
</Update>

<Update label="v0.13.0" description="2026-09-27">
  *The first big release in preparation for Conduit Cloud: workspaces own their email domains, sign-in methods and login pages, invites replace the join policy, and telemetry keeps member content and identity out — read the upgrade notes before pulling.*

  ## Upgrade notes

  Creating or updating a workspace through the Admin API or the MCP workspace
  tools refuses a slug that isn't lowercase letters, numbers, and single
  hyphens; existing slugs are kept until changed.

  A connector tool with invalid `x-mcp-header` annotations stops being listed
  after the upgrade (it was previously offered with the annotations removed).
  The server log names each such tool and why; the connector's provider needs
  to fix the tool definition for it to return.

  Email domains move from each workspace identity provider to the workspace.
  The migration carries every domain over with its verification and the
  provider it signs in through. While a rolling upgrade runs, a domain change
  made through a replica still on the previous version is lost, and replicas
  still on the previous version keep routing domains as they were before the
  upgrade. Rolling back to the previous version shows domains as they were
  before the upgrade until you upgrade again.

  In a multi-workspace deployment, the login page sends an email at a domain a
  workspace has verified to that workspace's provider, and offers the password
  field for it only when that provider can't be reached. If
  `CONDUIT_ADMIN_EMAIL` (the break-glass login) is at such a domain, change it to
  an address no workspace verifies, such as the default `admin@localhost`, so
  break-glass sign-in doesn't depend on that provider being down. The same holds
  for any password account at a verified domain.

  The workspace join policy is removed. People become members of a workspace
  through its own identity provider at its domains, an invite, or SCIM, so a
  workspace set to let anyone signed in join, to admit a join link, or to admit
  people at an email domain no longer does. Members are unaffected. An invite
  reaches only a domain the workspace has verified or the inviting admin's own,
  and needs email set up; people elsewhere, such as contractors at another
  company or at a public email service, join through SCIM. A domain the
  workspace held only to admit joins (unverified, signing in with no
  provider) is removed from its **Domains**. While a rolling upgrade runs,
  replicas still on the previous version keep admitting joins by the join
  policy; rolling back restores it, though not `settings:read` on API
  clients.

  A session signed in through an identity provider stops working when the
  provider is disabled or deleted, and one signed in by password stops working
  when password sign-in is turned off (an instance admin's excepted), in the web
  app and in MCP clients. Every workspace otherwise accepts every sign-in method
  until an admin changes that. Sessions and MCP tokens from before the upgrade
  recorded no sign-in method, so turning off a provider or password sign-in
  doesn't end them: they last until they expire (an MCP client's refresh token
  renews for up to 90 days at a time). To end them sooner, for instance when
  turning off a compromised provider, set a re-authentication age on each
  workspace (**Settings → Sign-in → Sign-in methods**), which asks every holder
  of such a session to sign in again; so does a workspace narrowing its
  methods. Instance admins aren't subject to a workspace's sign-in methods.

  Admin API clients: some identity provider and workspace fields change
  incompatibly. `IdentityProvider.domain_status` is removed; read a domain's
  verification from `ListWorkspaceDomains` (`sso:read`).
  `VerifyIdentityProviderDomain` and its `verify_workspace_idp_domain` tool are
  removed in favor of `VerifyWorkspaceDomain`. `CreateIdentityProvider` refuses
  a workspace provider with no `domains`. `GetAuthPolicy` answers only instance
  admins; anonymous callers read the login page's methods from
  `GetAuthMethods`. `SetActiveWorkspace` takes `workspace_id` in place of
  `organization_id`; a caller still sending `organization_id` sends no
  workspace and is refused. `GetWorkspaceJoinPolicy` is removed, and with it
  the `settings:read` scope, which is taken off every API client that held it;
  a token request that still names it is refused with `invalid_scope`.

  Building from source needs Go 1.27.1 or later. If you set `GOLANG_IMAGE` for
  the Docker build, point it at a `golang:1.27.1` image or later.

  Signing in through a workspace's own identity provider now makes someone a
  member only with an email the provider confirms at one of its domains. A
  workspace provider with no domains lets no one new join after the upgrade;
  members who have signed in through it before, or that SCIM provisioned,
  still sign in. Add the domains your members' emails are at in the provider's
  dialog, under **Domains**; they don't need to be verified for this, unless
  the provider doesn't send `email_verified` (Microsoft Entra ID doesn't): then
  verify them. Contractors at domains you don't list join through SCIM.

  If your workspace verified an email domain and later removed its
  `conduit-domain-verification` TXT record, publish it again: another workspace
  that publishes its own record on the domain can now take the verification
  over while yours is missing.

  Telemetry no longer carries tool content or upstream error text from member
  calls. Tool-call, prompt-fetch and resource-read events report failures as
  `error.type` (with `audit.reason` set to the same value) instead of the
  error message, resource reads carry `mcp.resource.scheme` instead of
  `mcp.resource.uri`, and the `mcp.upstream.request_body`,
  `mcp.upstream.response_body` and `mcp.upstream.error_message` span
  attributes and the entry span's `url.path` are gone (`http.route` remains).
  Dashboards or alerts built on those attributes need updating.

  Spans and events no longer carry `user.email` (or email under
  `enduser.id`). Use `user.id`; the audit log keeps the emails, and its
  entries now carry `actor_user_id` to join them (see Added). A SIEM that
  keyed on `user.email` should key on `user.id`. The names of a workspace's
  own settings (`policy.name`, `group.name`, `idp.name`, `scim_token.label`,
  `api_client.name`, `rule.name`, `scim.idp_name`) are no longer sent to
  instance exporters, which get the ids already on each event; the
  workspace's own exporters still receive them.

  ## Added

  * **Workspace invites.** Workspace admins invite people by email from
    **Settings → Members** (**Invite**), where pending invites are listed to
    resend or cancel; the API is `CreateWorkspaceInvite`, `ListWorkspaceInvites`
    (with the domains the caller may invite at, `invitable_domains`),
    `ResendWorkspaceInvite` and `CancelWorkspaceInvite`.
    An invite goes to an address at the admin's own verified domain or one the
    workspace verified, with a role up to admin and the admin's own. Its link
    goes only into the email, so invites need email configured. The link opens
    the workspace's login page, and the invitee joins with the invite's role by
    signing in with that address (`JoinWorkspace` takes an `invite_token`);
    someone with no account, invited to a workspace that accepts email and
    password, creates a password account from the invite
    (`GetWorkspaceInvite`, `AcceptWorkspaceInvite`). An invite grants no more than the
    admin who last sent it could grant when it's accepted. While password sign-in
    is on (the default), this means password accounts can now be created by
    anyone a workspace admin invites, with the invitee's consent through the
    emailed link, not only by first-run setup; turn password sign-in off to
    prevent it. A migration adds the `workspace_invites` table. See
    [Access control](/docs/conduit/configure/access-control#invites).

  * **Workspaces own their email domains.** The workspace **Settings → SSO**
    page is now **Settings → Sign-in**, with the workspace's identity providers
    and a **Domains** list below them: add a domain and verify it with a DNS
    TXT record; each provider's dialog sets which domains sign in with it. The
    API has `ListWorkspaceDomains`, `AddWorkspaceDomain`,
    `DeleteWorkspaceDomain` and `VerifyWorkspaceDomain`, with matching built-in
    admin tools; API clients with `sso:read` can list domains, without their DNS
    challenge. See [SSO](/docs/conduit/configure/sso).

  * **The multi-workspace login page asks for your email first**, sending an
    address at a workspace's verified domain straight to that workspace's
    provider, and leads with the method the browser last signed in with. See
    [SSO](/docs/conduit/configure/sso#the-main-login-page).

  * **A workspace chooses which sign-in methods it accepts** under **Settings →
    Sign-in → Sign-in methods**: its own providers (accepted while enabled),
    each of the instance's providers, and password. Its login page offers
    exactly the methods it accepts. It can:

    * admit an instance provider, such as Google, only for accounts at its own
      domains, and send those domains' sign-ins straight to it;
    * ask members to sign in again after a day, a week or a month.

    A session the workspace doesn't accept is asked to sign in again, and an
    MCP client is sent to authorize again, where the consent screen says which
    workspace needs a fresh sign-in. Disabling or deleting a provider signs out
    everyone who signed in through it, and turning password sign-in off for the
    instance signs out everyone who signed in by password, except instance
    admins. Conduit refuses a change that would leave
    a workspace with no sign-in method, or lock out the admin making it. The API
    has `GetWorkspaceSignInPolicy`, `UpdateWorkspaceSignInPolicy` and
    `UpdateWorkspaceInstanceProvider`. See
    [SSO](/docs/conduit/configure/sso#which-sign-in-methods-a-workspace-accepts).

  * **Every workspace has a login page** at `/login/<workspace-id>` (the id
    in its MCP URL), offering its sign-in methods and starting members'
    sessions in that workspace. See [SSO](/docs/conduit/configure/sso#the-workspace-login-page).

  * **Conduit speaks the stateless MCP protocol revision `2026-07-28`**
    alongside the handshake revisions `2025-03-26`, `2025-06-18`, and
    `2025-11-25`, selected per request. `server/discover` now lists
    `2026-07-28`, so clients on that revision use it directly instead of
    falling back to the `initialize` handshake. On it, clients learn about tool,
    prompt, and resource list changes over a `subscriptions/listen` stream;
    per-resource update subscriptions are not offered. Clients on the handshake
    revisions are unaffected. See the
    [MCP client reference](/docs/conduit/use/mcp-reference).

  * **The load-testing harness measures either protocol revision.**
    `conduit-bench -era stateless` (or `ERA=stateless` for k6) drives every
    scenario over MCP `2026-07-28`, holding `subscriptions/listen` streams.
    See [Load Tests](/docs/conduit/load-tests#protocol-revisions) for the comparison
    with the handshake revisions.

  * **Connector progress reaches your client.** When a client connected over
    HTTP asks for progress on a tool call (a `progressToken`) and accepts a
    streamed response, the connector's progress notifications are relayed to it
    as the call runs, on every protocol revision. Connectors that don't report
    progress are unaffected.

  * Audit log entries carry `actor_user_id`, `trace_id` and `span_id`, so an
    audited action joins to telemetry by user id and trace. Returned by
    `ListAuditLog`, `ListOrgAuditLog` and the `audit:read` API. Existing rows
    leave them empty. The migration runs automatically on boot.

  * `ListRecentToolCalls` and `ListOrgRecentToolCalls` accept a `trace_id`
    filter: from a span in your telemetry backend to the call's stored error
    text. A usage-store migration adds the index automatically on boot.

  ## Changed

  * **Workspace slugs are lowercase letters, numbers, and single hyphens, at
    most 63 characters**, and a workspace created without one gets a short
    random suffix instead of failing when another workspace holds the slug its
    name derives.

  * **The settings sidebar groups pages the same way in multi-workspace
    deployments as in single-workspace ones**: Access Control, Authentication &
    API, and Observability, with an **Instance Admin** section below for
    instance admins. A workspace's name is edited from **Workspace Settings** in
    the workspace menu.

  * **`SetActiveWorkspace` takes `workspace_id`** in place of
    `organization_id`. Switching into a workspace that doesn't accept how you
    signed in now succeeds, and the web app then asks you to sign in to it
    again.

  * **A verified domain can move to the workspace that now controls its DNS**:
    one that publishes its own TXT record while the current holder's is gone
    takes the verification over. Keep your record published to keep the domain.
    See [SSO](/docs/conduit/configure/sso#instance-sign-in-vs-workspace-sso).

  * **Joining a workspace is through its own provider, an invite, or SCIM.**
    The join policy (anyone signed in may join, listing, the join link, and
    join domains) is removed, along with `GetWorkspaceJoinPolicy`,
    `UpdateWorkspaceJoinPolicy`, `GetWorkspaceJoinLink` and their built-in
    tools, `JoinWorkspace`'s `link_token`, and the `settings:read` API-client
    scope. `DiscoverWorkspaces` lists the workspaces a sign-in offered you. See
    [Access control](/docs/conduit/configure/access-control).

  * **Single-workspace deployments get a workspace's sign-in settings on
    Settings → Sign-in**: each provider's **Domains** action, which can limit
    it to accounts from your domains, the **Domains** list, and **Ask members
    to sign in again**. See [SSO](/docs/conduit/configure/sso#instance-sign-in-vs-workspace-sso).

  * A connector tool whose `x-mcp-header` annotations are invalid (empty, not a
    header name, duplicated, on a parameter that isn't a string, integer or
    boolean, or placed outside the schema's properties) is no longer offered,
    matching how MCP `2026-07-28` clients treat such a tool. The connector's
    other tools are unaffected, and a warning names the tool and the reason.

  * OAuth connectors also request any scope an MCP upstream's `401` challenge
    names, alongside the scopes it advertises.

  * **The auth policy is readable only by instance admins.** `GetAuthPolicy`
    no longer answers anonymous callers. The login page gets its identity
    providers from `GetAuthMethods`, and whether the caller may create a
    workspace is `can_create_workspace` on `GetSession`. Instance admins can
    read the policy with the new `get_auth_policy` built-in admin tool.

  * **The auth policy's `signup` and `idp_required` fields are removed.** Neither
    was ever enforced: setting them changed nothing about who could sign in.
    Clients that send them have them ignored.

  * **Only your domains' people join through a workspace provider.** Signing
    in through a workspace's own provider makes someone a member only when the
    provider confirms their email at one of its domains, verified or not (a
    provider that doesn't send `email_verified`, such as Microsoft Entra ID,
    only at a verified one); anyone else it authenticates needs an account it
    already signs in, and the rest are refused before an account is created.
    At a verified domain they join as they sign in; at one the workspace
    hasn't verified, the sign-in ends on a **Join Acme?** page and they join
    only if they choose to (`JoinWorkspace`, which `DiscoverWorkspaces` now
    lists as joinable). A workspace provider needs at least
    one domain (`domains` on `CreateIdentityProvider` and
    `UpdateIdentityProvider`). The provider dialog reads the provider's
    published claims (`GetIssuerClaims`) to warn when it doesn't confirm email
    addresses, which joining relies on. See
    [SSO](/docs/conduit/configure/sso#who-joins-through-a-workspace-provider).

  * **A provider's domains are workspace domains.** An identity provider's
    `domains` (on create, update, and read) are the workspace domains that sign
    in with it; one named on the provider that the workspace doesn't hold yet
    is added, unverified. `domain_status` is removed in favor of
    `ListWorkspaceDomains`. `VerifyWorkspaceDomain` replaces
    `VerifyIdentityProviderDomain` (and the `verify_workspace_idp_domain` tool).
    A verified domain belongs to one workspace, signs in with at most one of its
    providers, and stays verified when that provider is removed. Public email
    services such as gmail.com can no longer be added as a domain.

  * **Sessions record how the person signed in.** Browser sessions and MCP
    access tokens now store the sign-in method, the identity provider, and the
    time of the sign-in behind them; a refreshed token keeps the original
    sign-in's time; a workspace's sign-in methods judge a session by them. A
    migration adds the columns to the sessions table and the OAuth code,
    refresh-token and device-code tables; existing rows record an unknown
    sign-in.

  * **Tool, prompt and resource change notifications cost Conduit no upstream
    calls.** A policy, group or role change notifies only the MCP clients whose
    access it moved, and a connector change notifies the clients that may use
    that connector, without Conduit listing any connector to check. Each
    notification now covers tools, prompts and resources together, so a client
    may re-list a list that didn't change. A tool a connector adds on its own
    side shows up the next time the client re-lists.

  * Conduit is built with Go 1.27.1. The standard library's faster JSON
    implementation cuts the gateway's per-request encoding cost, most for large
    tool results.

  * Telemetry is stricter about what it contains: no tool arguments, results or
    upstream bodies, no recipient addresses in email delivery errors, connector
    URLs reduced to `scheme://host` (with a new `connector.id` attribute), and
    capped, valid-UTF-8 text everywhere. A failed MCP call's event carries an
    `error.type` in place of the error text. Names of a workspace's policies,
    groups, identity providers, SCIM tokens and API clients reach only that
    workspace's exporters; instance exporters get their ids. Connector names
    still reach every exporter. See
    [What telemetry contains](/docs/conduit/configure/telemetry#what-telemetry-contains).

  * Failed sign-in audit rows record the actor as `anonymous` and keep the typed
    identifier only when it is shaped like an email address; sign-in audit rows
    now include the (capped) user agent.

  * Connector URLs in audit log details no longer include credentials or query
    strings.

  * **Reading a connector's answer costs less.** Conduit parses each tool
    result, listing, prompt, and resource a connector returns once instead of
    three times: a 1 MiB tool result takes about a third of the CPU and 40% of
    the memory it did, and a large tool listing 30–40% less of each.

  * **One member's connector token refresh no longer holds up everyone
    else's calls.** Conduit gets connector tokens for each member (and for a
    workspace-wide grant) independently, so a slow or unreachable token
    endpoint delays only the calls waiting on that token, and a call whose
    token is already cached never waits.

  ## Fixed

  * Email sent over SMTP carries a plain-text part alongside its HTML, and a
    subject or sender name with non-ASCII characters (such as a workspace
    name) is encoded, so mail servers that require it no longer reject or
    garble the message.

  * Signing in through an identity provider from a page that sent you to the
    login page, such as the device authorization page for the Conduit CLI,
    returns you to that page instead of the home page.

  * The home page's tool-calls card and top connectors and tools cover the
    active workspace, like the cards beside them, and the tool-calls headline
    counts the last 30 days its label names. It counted every workspace's calls
    of all time.

  * Someone signing in for the first time gets a personal workspace even when
    another person with the same name already has one.

  * Instance admins can create a workspace from the workspace switcher while
    workspace creation is limited to instance admins. The option was disabled
    for everyone in that mode.

  * A JSON-RPC message without an `id` that names a request method (anything
    but a `notifications/…` method) is refused with HTTP 400 instead of being
    run with its result discarded, and a notification that carries an `id` is
    refused as an invalid request, as is a request whose `id` isn't a number or
    a string of at most 256 bytes. The Conduit CLI's local server applies the
    same rule to id-less requests, dropping one instead of running it.

  * A connector's result metadata (`_meta`) on a rendered prompt now reaches
    the client, as it already did for tool results.

  * A single malformed byte in a logged value — reachable without signing in —
    could make an OpenTelemetry collector reject a whole batch of logs and
    events. Every exported string is now valid UTF-8 and bounded.

  * Upstream response bodies, including one-time Pipedream connect links, were
    recorded on spans; they no longer are.

  * The docs claimed tool calls are recorded in the audit log without error
    text. Tool calls are recorded in usage (denied calls also in the audit log),
    and the access-control, connector and Pipedream pages now say so.

  * Large integers in a URL connector's `structuredContent`, result `_meta`,
    and content-block fields reach an MCP client connected to Conduit directly
    exactly as the connector sent them. They were rounded to about 16
    significant digits.

  * Connecting your own account or token for an instance-wide connector now
    refreshes your MCP clients' tool lists, as it already did for a workspace's
    connectors.

  ## Security

  * The login page looks up where an email signs in (`ResolveSignIn`) with a
    POST, not a GET, so the address someone types no longer lands in the
    query string that proxies and access logs record.

  * A SCIM rename decides afresh whether the account's new email counts as
    verified, as account creation does: for a workspace's own identity
    provider, only at a domain the workspace verified and bound to it. The
    address kept the old one's verification before, so a workspace could
    rename an account into another organization's domain and have it count as
    verified there.

  * A sign-in through an identity provider Conduit can't reach no longer shows
    the signed-out visitor the provider's address and the network error.

  * Responses from identity providers during sign-in (OIDC discovery, signing
    keys, user info) are capped at 1 MiB, like the other control-plane responses
    Conduit reads, so a hostile or compromised provider can't stream an
    unbounded body into server memory.

  * Creating an MCP OAuth connector now fails, with "metadata at … names issuer
    …, not …", when the authorization server's metadata names a different
    issuer than the server the upstream points to (RFC 8414 §3.3), which is
    how an authorization-server mix-up is staged. Existing connectors are
    unaffected; an upstream that serves mismatched metadata needs its provider
    to fix it.

  [Compare v0.12.0...v0.13.0](https://github.com/PipedreamHQ/conduit/compare/v0.12.0...v0.13.0)
</Update>

<Update label="v0.12.0" description="2026-09-23">
  *Workspaces decide which MCP clients may connect and which connect tabs their home page offers, replacing CONDUIT\_CIMD\_ALLOWED\_DOMAINS — read the upgrade notes before pulling.*

  ## Upgrade notes

  `CONDUIT_CIMD_ALLOWED_DOMAINS` is removed, and an instance that still sets it
  refuses to boot until it is unset. Which clients may connect is now each
  workspace's decision, and every workspace starts open to any client: before
  upgrading an instance that relied on the variable, plan the equivalent in
  Settings → MCP Clients for each workspace — set **Who can connect** to *Only
  allowed clients* (the clients Conduit recognizes connect when they prove who
  they are, and anything self-registered needs an explicit Allow), block any
  recognized vendor you do not accept, and **Add a client** for a vendor Conduit
  does not know, by the URL of its client ID metadata document. See
  [Governing connected clients](/docs/conduit/use/mcp-authorization#governing-connected-clients).

  The instance-level MCP Clients page is removed. Every client decision — who
  may connect, per-client allow and block, revocation — is made on each workspace's
  MCP Clients page (instance admins can act in any workspace), and the Dynamic
  Client Registration switch now lives in **Settings → Sign-in**. The
  instance-wide client list and its wholesale revocation have no replacement:
  to stop a client everywhere, block it in each workspace, which refuses every
  member's use of it at once.

  A database migration creates the workspace client tables and seeds each
  single-workspace member's clients into that workspace's list; it runs
  automatically on boot, and the list fills further as clients make their next
  request.

  The new provider setting is added by a database migration on startup. Existing
  Okta, Microsoft, and Custom OIDC providers continue to request fresh sign-in until
  an admin changes the setting. Google and GitHub presets omit `prompt` after the
  upgrade regardless of the stored setting; no configuration change is needed.

  ## Added

  * **Workspace MCP Clients.** Workspace administrators get an MCP Clients page
    (Settings → MCP Clients, in both deployment shapes) that decides which
    clients may connect to the workspace and lists every client it knows or has
    seen; see
    [Governing connected clients](/docs/conduit/use/mcp-authorization#governing-connected-clients).
  * **Who can connect** is one choice among three — any client, verified clients
    only (URL-based client IDs and the Conduit CLI), or only allowed clients (the
    clients Conduit recognizes when they prove who they are, plus whatever the
    workspace allows) — with the workspace's own message to blocked members,
    shown in the client's error, on the consent screen, and on the home page.
  * The consent screen lists the workspaces that will refuse the client before
    the member approves it.
  * The **Clients** table is one row per client the workspace knows or has seen
    — the ones Conduit recognizes (Claude Code, Claude Desktop, Cursor, Codex,
    ChatGPT, VS Code, MCP Inspector, the Conduit CLI), the ones the workspace
    adds, and anything that has connected, admitted or refused — each with its
    access and the reason, its home page tab, how many members use it, their
    live sessions, and when it was last active there; facets select what is
    allowed, blocked or connected.
  * A recognized client's installs that prove they are it and the installs that
    only claim to be it are separate rows, each allowed or blocked on its own.
  * Any client can be allowed or blocked from its row, a block with its own
    message.
  * **Add a client** names a client Conduit does not know by the URL of its
    client ID metadata document, which only its vendor can host; the workspace
    decides about that client on its own, even when the document sits under a
    vendor Conduit recognizes (one connector among ChatGPT's, say).
  * A workspace can revoke a member's client for that workspace alone: the
    member's token keeps working at their other workspaces, its next refresh is
    refused so the client returns to the consent screen, and access returns
    once they approve it again.
  * The client list starts with the clients already authorized by members who
    belong to just that workspace (every member on a single-workspace
    instance); a member of several workspaces appears as their clients connect
    after the upgrade.
  * Conduit recognizes the shipped clients from the client ID metadata documents
    their vendors publish (verified when they connect with one) or, for clients
    that register themselves (Cursor, MCP Inspector), from what they present,
    which any client can imitate, so those stay unverified; repeat registrations
    of one client are grouped by an identity the server computes from the
    client's `software_id`, else its redirect URIs, else its client id.
  * Rejected MCP requests are counted under the new `client_revoked` and
    `client_not_allowed` reasons.
  * **The home page's connect tabs are the workspace's.** The tabs under *How to
    use Conduit* are the clients the workspace offers — the ones Conduit has
    setup steps for, now including **VS Code**, and any client the workspace
    added — in the workspace's order, the first shown, allowed tab opening by
    default.
  * **Edit home page tab** gives a client its own icon and Markdown steps with a
    live preview; `{{server_url}}` and `{{client_name}}` are filled in when
    shown, fenced code becomes copyable commands, the steps can't be saved
    blank, and a menu action restores Conduit's.
  * The workspace arranges the tabs by dragging rows in the Clients table and
    hides a tab with its house toggle without changing whether the client may
    connect; a blocked client has no tab, and when the posture itself blocks,
    Home says so above the tabs in the workspace's words.
  * Okta, Microsoft, and Custom OIDC providers have a **Request fresh sign-in**
    setting. Turn it off to allow the provider to reuse an existing session when
    signing in to Conduit.

  ## Changed

  * **MCP client governance is the workspace's alone.** The instance-level MCP
    Clients page — the roll-up of every authorization across workspaces, with
    wholesale revocation — is removed, and its two RPCs
    (`AdminListOAuthClients`, `AdminRevokeOAuthClient`) with it. Which clients
    may connect, per-client decisions and revocation are made on each workspace's
    MCP Clients page; the Dynamic Client Registration switch moves to
    **Settings → Sign-in**, since accepting registrations is the authorization
    server's setting, not a workspace's. What crosses workspaces — registrations,
    consents, use — stays visible in the instance audit log and usage pages.

  ## Fixed

  * **`server/discover` declaring an unsupported protocol version is refused
    per the MCP spec.** A probe that names a stateless-era revision Conduit does
    not serve (such as `2026-07-28`) now gets HTTP 400 with
    `UnsupportedProtocolVersionError`, whose `supported` list names the
    revisions Conduit does serve, like every other request declaring that
    version. Clients retry with a listed version; a probe that declares no
    version is still answered with the list.
  * Google and GitHub presets now omit `prompt`, since neither provider documents
    support for `prompt=login`.
  * **An upstream that keeps rejecting freshly issued credentials no longer
    costs a token mint or refresh per request.** A credential obtained in the
    last 30 seconds that the upstream rejects is kept rather than replaced, so
    such an upstream costs at most one mint or refresh per half minute per
    credential however many requests arrive; the requests fail with the same
    reconnect prompt as before. A per-user OAuth token an upstream rejects
    before its stated expiry is now refreshed and the call repeated with the
    new token, instead of being presented again. A connector with a fixed
    token, or none, no longer re-handshakes and repeats a call the upstream
    answered 401: the call fails as an authentication failure and the next one
    opens a fresh session.

  [Compare v0.11.1...v0.12.0](https://github.com/PipedreamHQ/conduit/compare/v0.11.1...v0.12.0)
</Update>

<Update label="v0.11.1" description="2026-09-18">
  *Traces group by operation and no longer report abandoned requests, oversized results or stalled upstreams as separate issues, repeated exporter errors are logged once a minute, membership and client listings are faster, and a token an upstream rejects is replaced at once.*

  ## Upgrade notes

  None.

  ## Changed

  * **Membership lookups by user are indexed** (a migration runs on upgrade), so
    listing a user's workspaces and resolving which workspace an MCP session
    belongs to no longer scan the membership table.
  * **Spans are grouped by operation in your tracing backend**: database spans
    carry the key APM backends read to name them `sqlite.query` or
    `postgresql.query`, internal spans name their domain as the operation
    instead of collapsing into one "Internal" bucket, and upstream MCP calls and
    outbound HTTP calls list the connector and method, or the peer, as their
    resource.
  * **A tool result over the gateway's response size cap is no longer a tracked
    issue**: the call still fails and tells the caller to request less, and the
    failed span stays visible in traces.
  * **A request the caller abandons is no longer a server error.** When a
    browser reload or a closed MCP stream cancels a request mid-flight, Conduit
    answers Canceled (HTTP 499) instead of a 5xx, so the failure it interrupted
    records no tracked exception in your tracing backend, writes no error-level
    log line, does not count against request error metrics, and sends no
    in-band MCP log message about it to the caller's other sessions.
  * **A dependency's failure now records its cause.** When Pipedream or an
    upstream connector refuses or fails a request — a rejected credential, a
    5xx, a timeout — the tracked exception in your tracing backend and the
    warning in the log carry what the dependency said, under an issue type of
    its own (`conduit.rpc.dependency_failed`) kept apart from Conduit's own
    faults, instead of only the status code and route.
  * **A failing telemetry exporter no longer floods the console log.** An
    export error that recurs on every batch — a collector that is unreachable,
    or one that does not accept a signal Conduit sends — is logged once, then
    once a minute with the number of repeats, instead of on every attempt.
  * **A collector that does not serve a signal is no longer sent it batch after
    batch.** When a gRPC collector answers `Unimplemented` for traces, logs, or
    metrics — a traces-only backend given Conduit's logs, say — Conduit reports
    it once, naming the exporter and the signal, stops sending that signal to
    that exporter, and probes with one batch every five minutes until the
    collector serves it, then reports that it is resuming. The exporter's other
    signals continue unaffected.

  ## Fixed

  * **Open MCP streams no longer flood the tracing backend with single-span
    traces**: a request's session check now appears inside that request's
    trace, and the periodic keepalive check on each open stream records none.
  * **The admin list of OAuth and MCP clients no longer slows down as clients
    accumulate**: it issued two database queries per registered client, over a
    second with a few hundred of them.
  * **A workspace's Pipedream access token is minted once and shared by every
    member's MCP session**, instead of once per member, removing a slow step
    from each member's first request.
  * **A Pipedream access token that Pipedream rejects is replaced at once.**
    When Pipedream answers a member's MCP request or a Connect API call with
    401 for a token Conduit still held as valid, Conduit drops that token, mints
    a fresh one and retries the request, instead of presenting the rejected
    token until its stated expiry and showing members a reconnect prompt for a
    credential that was never theirs.
  * **GraphQL and OpenAPI connectors recover from a rejected token the way MCP
    connectors do.** When such a connector's upstream answers 401 for a
    client-credentials or per-user OAuth token Conduit still held as valid, the
    token is dropped and the call repeated once with a fresh one, instead of
    failing with a reconnect prompt until the token's stated expiry.
  * **Tool calls by connector-qualified name from MCP Apps-capable clients no
    longer fan a tool listing out to every connector first**, which slowed those
    calls and put listing load on every upstream.
  * **An upstream that stalls past Conduit's request timeout now appears as one
    issue in your tracing backend**, instead of two to four issues for the same
    failure under differing messages.
  * **A credential Conduit cannot obtain is no longer reported as your session
    having expired.** When Conduit itself failed to get the token it uses for an
    upstream — the Pipedream token mint refused or unreachable, a connector's
    client-credentials grant failing, a token endpoint down — MCP clients were
    told their session had expired and re-authenticated for nothing, tool calls
    failed with "session expired", and the failure left no tracked issue. It is
    now reported as an unavailable upstream credential: the tool call says so
    and that reconnecting will not help, the connector catalog shows the
    connector as unavailable rather than as needing a credential, and the
    failure is tracked under its own issue type. A grant the upstream itself
    refuses, because it was revoked or expired, still asks the caller to
    reconnect.
  * **Non-ASCII text in a tool result or upstream error no longer makes the
    collector reject traces and events.** Text Conduit shortens before putting
    it on a span, an event, or a stored record — an upstream response body, an
    error message, a client name — was cut at a fixed byte count, which could
    split a multi-byte character and leave invalid UTF-8; the collector then
    refused the whole batch it arrived in. Shortened text now ends on a
    character boundary and is marked with an ellipsis, and text an upstream
    sends that is not UTF-8 is repaired before export.

  [Compare v0.11.0...v0.11.1](https://github.com/PipedreamHQ/conduit/compare/v0.11.0...v0.11.1)
</Update>

<Update label="v0.11.0" description="2026-09-14">
  *Workspace RPCs and MCP admin tools now name the workspace they act on, identity providers are one surface for instance and workspace scopes, and workspace admins can publish help instructions.*

  ## Upgrade notes

  The database migration for workspace help runs automatically at startup.

  **Admin API: `ListWorkspaceIdentityProviders` is replaced by
  `ListIdentityProviders`.** The identity-provider RPCs now serve both scopes
  from one family, so the workspace-only method is gone. API clients holding
  `sso:read` call `ListIdentityProviders` instead; as on every workspace method,
  `organizationId` is optional for an API client and means its own workspace. In
  responses, the provider's
  `owner_scope` field is now `organization_id` (empty for an instance provider).
  This breaks the scoped-method freeze deliberately, while the Admin API is days
  old and the project is pre-1.0; the OpenAPI reference is regenerated.

  **MCP builtin workspace-admin tools take an `organizationId` argument.** Omit
  it and the tool acts on the workspace the MCP session is bound to; name another
  workspace you administer to act there instead.

  ## Added

  * Workspace admins can write Markdown help instructions from the question-mark
    icon in the header (**?** → **Edit**). Members see the icon once instructions
    are saved.
  * `GetSession` returns `activeWorkspaceHelpSet`: whether the active workspace has help instructions.

  ## Changed

  * **Admin API: every workspace method takes `organizationId`.** The 20
    workspace methods that had no such field — policies, groups, access
    inspection, workspace connectors, Pipedream, SCIM tokens and provisioning —
    gain it. For an API client the field is optional on every workspace method
    and means the client's own workspace; naming any other workspace is
    refused. Nothing an existing client sends stops working; the OpenAPI
    reference is regenerated.

  * **Workspace RPCs act on the workspace the request names.** Every
    workspace-level RPC requires `organization_id`, and a signed-in user's
    active workspace is never substituted for it — not by the authorization
    gate, the handler, the audit row, or the telemetry scope. A settings page left open
    after switching workspaces in another tab now changes the workspace it
    shows, not the one switched to. A refused call is audited in the workspace
    the request named; a member's refused instance-admin call, which names
    none, now lands in the instance audit log rather than the member's active
    workspace's. A workspace call that names no workspace is refused as
    malformed (`invalid_argument`) before any role check, and is not audited as
    a denial. An instance admin naming a workspace that does not exist gets
    `not_found`. In the app, the workspace settings pages need a workspace to
    address, so an instance admin who belongs to no workspace no longer sees
    them in the navigation.

  * **One identity-provider surface for instance and workspace.** The
    login-page providers (instance admins, **Settings → Sign-in**) and each
    workspace's SSO providers (workspace admins, **Settings → SSO**) were two
    RPC families and two settings pages over one table. They are now one family
    — `ListIdentityProviders`, `CreateIdentityProvider`,
    `UpdateIdentityProvider`, `DeleteIdentityProvider`,
    `VerifyIdentityProviderDomain` — addressed by scope: `organization_id` names
    a workspace, and leaving it unset names the instance, which only instance
    admins may do. Both settings pages render the same provider list, so the
    workspace SSO page gains the standard row-actions menu and the copyable
    redirect URI field the instance page already had. Over MCP the two scopes
    stay two sets of tools, named as in the previous release: `list_idps`,
    `create_idp`, `update_idp` and `delete_idp` in the instance-admin connector
    take no workspace, and `list_workspace_idps`, `create_workspace_idp`,
    `update_workspace_idp`, `delete_workspace_idp` and
    `verify_workspace_idp_domain` in the workspace-admin connector default to
    the session's workspace like every other workspace tool. A workspace
    admin's tool list never advertises the instance's providers.

  * **Identity-provider changes made in a workspace now appear in that
    workspace's audit log.** Creating, updating, deleting, or verifying a domain
    of a workspace SSO provider is recorded against the workspace; instance
    provider changes stay instance-level. Domain verification also joins the
    audit event catalog, so the filter can select it.

  * **Workspace telemetry exporters receive an instance admin's
    workspace-filtered usage reads.** On the instance usage dashboard, a read
    filtered to one workspace is now scoped to that workspace, so the workspace's
    exporter receives it, as it already does an instance admin's read of the
    workspace's audit log. Unfiltered, instance-wide reads still reach instance
    exporters only.

  * **The instance usage dashboard's workspace filter must name an existing
    workspace.** A usage read filtered to a workspace that has since been
    deleted is refused as not found, like every request that names a missing
    workspace. The filter menu offers live workspaces only, so this reaches only
    a saved URL.

  * User docs start with short setup and connector guides, link directly to
    Conduit pages, and include a short [explanation of MCP](/docs/conduit/use/what-is-mcp).

  * User guides cover remote setup without the feature-flagged Conduit CLI and
    direct help and connector requests to Conduit admins.

  * The Connectors page links to the connector-request guide beside its description.

  * The book icon moved from the sidebar footer to the header and opens
    [Start here](/docs/conduit/use/start). The GitHub links in the app and docs are gone.

  * Home's Claude and ChatGPT setup steps follow the providers' current menus.

  ## Fixed

  * **Editing an instance identity provider now reports what went wrong.** A
    rejected issuer or token URL (plain `http`, a special-use address) or a
    scope list without `openid` returns an invalid-argument error with the
    reason, where the instance path previously answered "identity provider not
    found".

  [Compare v0.10.3...v0.11.0](https://github.com/PipedreamHQ/conduit/compare/v0.10.3...v0.11.0)
</Update>

<Update label="v0.10.3" description="2026-09-10">
  *Adds two repository-built images for load-testing a throwaway Conduit deployment on your own infrastructure, and stops a connection's menu from offering Replace token when there is nothing to type.*

  ## Upgrade notes

  None.

  ## Added

  * **Load tests on your own infrastructure.** Two new images built from the
    repository — `loadtest-conduit`, the Conduit image with a load-test seeder
    that runs at boot, and `loadtest-mcp-server`, the mock upstream the seeded
    connectors call — let you load-test a throwaway deployment wherever you
    run Conduit and its upstreams, with every result recording the pod shape
    it was measured on; see [Load Tests](/docs/conduit/load-tests).

  ## Fixed

  * A connection's menu no longer offers **Replace token** when there is nothing
    the member could type. A connector whose stored auth method is outside the
    `none` / `token` / `oauth` vocabulary now reads as unauthenticated everywhere
    (which is how the gateway already ran it), so a stale per-user token for it
    no longer lists it on **Accounts** as a live connection; and a member whose
    own token has been displaced by a workspace credential they may not override
    sees only **Disconnect**. Before, either case opened an update dialog with no
    inputs and a disabled Save.

  [Compare v0.10.2...v0.10.3](https://github.com/PipedreamHQ/conduit/compare/v0.10.2...v0.10.3)
</Update>

<Update label="v0.10.2" description="2026-09-07">
  *Workspace administrators can send their workspace's traces and events to their own OpenTelemetry collectors, MCP clients that support MCP Apps can render connector UI templates through the gateway, and each non-admin user may own one workspace.*

  ## Upgrade notes

  None.

  ## Added

  * **Workspace telemetry exporters.** Workspace administrators can send their
    workspace's traces and events to their own OpenTelemetry collectors from
    Settings → Telemetry. A workspace exporter receives only activity within
    that workspace — its members' MCP traffic, its settings changes, its SCIM
    provisioning — never other workspaces or instance administration, and never
    metrics. Endpoints must be `https` and use an HTTP protocol; they are
    reached through the same hardened outbound client as connectors. Signals
    sent to a workspace's exporters carry the resource attributes the workspace
    configures (`service.name` defaults to `conduit`); the deployment's
    `OTEL_RESOURCE_ATTRIBUTES` reach instance exporters only. Instance
    exporters continue to receive everything. A workspace may configure one
    exporter. See
    [Telemetry Export](/docs/conduit/configure/telemetry).
  * Every workspace-scoped span and event now carries a `conduit.workspace.id`
    attribute, so an instance backend can be filtered by workspace.

  ## Changed

  * The MCP gateway now preserves standard tool output schemas, structured tool
    results, and tool, result, and resource metadata from upstream connectors.
  * MCP clients that support MCP Apps can now discover and render UI templates
    from remote connectors. Conduit preserves the required `ui://` scheme while
    namespacing template URIs, forwards the Apps declaration through the local
    CLI, keeps app-only tools out of clients that cannot render them, and lets app
    views call uniquely named tools and load uniquely declared templates by the
    local names and URIs they were authored with. Undeclared secondary UI
    resources are resolved across every accessible connector currently
    advertising an app template, without a fixed connector limit and with
    explicit errors for ambiguous or incomplete results.
  * Telemetry exporter settings now reject an exporter with a missing name or
    endpoint, or an unknown protocol, instead of saving it and silently
    exporting nothing.
  * Workspace telemetry pipelines are now created on first use, with each replica
    retaining the 64 most recently used pipelines and recycling older ones. This
    bounds exporter queues and background workers without limiting how many
    workspaces may configure telemetry.
  * Each user may own one workspace. Membership in other users' workspaces is
    unaffected, and instance administrators are not limited.

  ## Fixed

  * Changing a workspace telemetry exporter's protocol or endpoint now clears its
    write-only headers instead of forwarding the stored credentials to the new
    collector.
  * An OTLP/HTTP telemetry exporter whose endpoint was entered with a trailing
    slash or with a signal path (such as `/v1/logs`) now sends traces to
    `/v1/traces`; before, they went to the path as entered.
  * The usage dashboards no longer drop the current hour's calls and auth
    failures (and, at midnight UTC, the current day's active users) when read
    during the first second of the hour.

  [Compare v0.10.1...v0.10.2](https://github.com/PipedreamHQ/conduit/compare/v0.10.1...v0.10.2)
</Update>

<Update label="v0.10.1" description="2026-09-03">
  *Active-active replicas no longer need session-sticky load balancing, so any L7 balancer works with its defaults; the Admin API can inspect a group's combined access, and MCP resource-read errors name the failing URI.*

  ## Upgrade notes

  * Sticky-session rules configured on a load balancer for Conduit's
    active-active tier can stay or go; neither choice affects correctness.

  ## Added

  * The Admin API can inspect the combined connector and tool access granted
    through a group, its parent-group hierarchy, and workspace-wide policies.
  * The MCP gateway's stateless-era (2026-07-28) surface now relays mid-call
    client interactions — form elicitation, sampling, and roots requests — as
    SEP-2322 `input_required` retries: round one returns the requests plus a
    sealed `requestState`, and a retry echoing the state with the matching
    `inputResponses` completes the original call. Multi-round chains work
    without re-sending earlier answers (they replay from the sealed state), and
    a call needing a capability the request's `_meta` didn't declare is refused
    with the spec's `-32021 MissingRequiredClientCapabilityError` naming the
    capability. The era remains served only when explicitly enabled, until its
    full semantics land.

  ## Changed

  * Active-active deployments no longer need session-sticky load balancing.
    A client's answer to an in-band MCP prompt (elicitation or sampling), and
    a request cancellation (`notifications/cancelled`), may reach any replica:
    the receiving replica hands it to the one holding the call, through the
    database and the cluster notification channel. Any load balancer that
    round-robins across replicas — an AWS ALB included — works with no
    affinity configuration, and the startup warning about sticky routing is
    gone. A client's answer to an in-band prompt is now capped at 1 MiB on
    every replica (a larger answer is refused and the prompt times out).
    Includes a database migration (runs automatically on boot).
  * The Admin API reference now presents read-only operations as HTTP `GET`
    without duplicate `POST` entries for the same methods.
  * MCP `resources/read` errors now echo the requested URI in `error.data.uri`
    (SEP-2164), in both protocol eras, so clients can attribute a failed read
    without parsing the error message.
  * The load-test documentation is one sizing-focused page (headline capacity,
    deployment sizing guidance, and a history table) instead of a page per
    test pass, and the published numbers are re-measured under a workload
    model calibrated to real production traffic: bursty agent calls, a small
    always-on bot cohort, heavy-tailed upstream latency, routine tool errors,
    and mixed response sizes.

  [Compare v0.10.0...v0.10.1](https://github.com/PipedreamHQ/conduit/compare/v0.10.0...v0.10.1)
</Update>

<Update label="v0.10.0" description="2026-08-27">
  *Adds a workspace Admin API with machine-to-machine API clients, modernizes MCP protocol support, and hardens upstream connector OAuth against token mix-up and confused-deputy attacks.*

  ## Upgrade notes

  A new database migration adds the API-client tables and a `kind` column to
  users; it applies automatically on boot (SQLite and Postgres). Optional: set
  `CONDUIT_DISABLE_API_CLIENTS=true` and restart Conduit to stop API-client
  token minting and reject existing machine tokens instance-wide (a break-glass
  kill switch; per-client controls are in workspace settings).

  ## Added

  * **Admin API with machine-to-machine auth.** Workspace admins can create
    **API clients** — machine principals that call Conduit's workspace-admin
    operations (connectors, policies, groups, audit-log reads) from scripts and
    CI with no user in the flow. Clients authenticate via OAuth 2.0 client
    credentials (`client_secret_basic` or, stronger, `private_key_jwt`) at
    `/oauth/token`, receive a short-lived bearer token, and call the same Connect
    RPC API the UI uses. Access is governed by area `read`/`write` **scopes**.
    API clients are RPC-only: they are not workspace members, hold no connector
    policies, and cannot connect to `/mcp` (headless MCP access is a planned
    follow-up). Managed in **Settings → API Clients**. See
    [Admin API](/docs/conduit/use/api).

  * **Admin API reference** — a generated, always-current reference for the
    public admin API (every method, its request/response fields, and an example),
    covering exactly the scoped surface an API client can reach. See
    [Admin API Reference](/docs/conduit/use/api).

  * **Connector lookup in the Admin API.** Connector list responses now include
    their canonical IDs, which automation can pass to `GetConnector` to retrieve
    one connector's non-secret configuration with `connectors:read`.

  * **Admin inventory and access-debugging APIs.** API clients can retrieve live
    connector tool catalogs with explicit resolution status, summarize configured
    policy grants, and inspect allow, enable, and effective access for one or many
    members. Sanitized SSO and provisioning inventory is available through the
    new `sso:read` and `provisioning:read` scopes; identity-changing operations
    remain machine-unreachable.

  * Upstream OAuth clients now authenticate at token endpoints with HTTP Basic
    (`client_secret_basic`, RFC 6749 §2.3.1) when that is the method the
    authorization server's metadata advertises for presenting a secret; the
    body shape (`client_secret_post`) remains the choice whenever it is
    advertised or the metadata is silent. The method is recorded when the
    connector's OAuth client is provisioned, so both the code exchange and
    token refreshes use it; connectors provisioned earlier keep the body shape
    until their next edit re-provisions the client.

  ## Changed

  * API-client create and manage dialogs now keep a stable size while scope
    details expand, continue directly into credential setup after creation, and
    show the client ID as a copyable value in the credentials panel — previously
    it was only visible when creating a client secret, leaving key-only
    (`private_key_jwt`) clients no way to see the ID their assertions must name.
  * The gateway's client to upstream MCP connectors now requests protocol
    revision `2025-11-25` at `initialize` (previously `2025-03-26`), accepts
    whichever supported revision the upstream negotiates, and echoes the
    negotiated revision on every later request's `MCP-Protocol-Version` header,
    as the spec requires of clients from `2025-06-18`. An upstream that
    negotiates a revision Conduit doesn't speak keeps working exactly as
    before: no header is sent, which the spec reads as `2025-03-26`.
  * Dynamic client registration for an upstream MCP connector's OAuth flow now
    declares the RFC 7591 `application_type` (`web` — or `native` when the
    instance's callback URL is a loopback address, the local-development
    shape), which authorization servers implementing MCP's SEP-837 require.
  * The MCP `initialize` response now assigns an `Mcp-Session-Id`, which
    stateful-transport clients echo on their later requests per the MCP spec.
    The id is advisory: Conduit authenticates every request by its bearer token
    and keeps MCP session state in the database, so requests are served
    identically with or without it — but clients that treat a missing
    assignment as "no session" (some strict SSE consumers) now get one.
  * MCP listings (`tools/list`, `prompts/list`, `resources/list`,
    `resources/templates/list`) are served in a deterministic sorted order, so
    an unchanged set always serializes identically — letting clients cache
    listings and improving LLM prompt-cache hit rates, per the MCP
    spec recommendation.
  * An MCP request that declares a stateless protocol revision (`2026-07-28` or
    later, via the `MCP-Protocol-Version` header or request `_meta`) is now
    answered with the spec's `UnsupportedProtocolVersionError` listing the
    supported versions — instead of a handshake-era-shaped response a modern
    client could misread. `server/discover` keeps answering for any declared
    version, so version discovery stays one round trip. Dual-era clients (the
    official MCP SDKs) fall back to the `initialize` handshake automatically.
  * MCP SSE responses now carry `X-Accel-Buffering: no`, telling buffering
    reverse proxies (such as nginx) to deliver event frames immediately.
  * `x-mcp-header` annotations in a connector's advertised tool schemas are
    stripped before the tools reach clients: Conduit does not support the
    custom-header feature (MCP 2026-07-28), so advertising the annotation would
    solicit `Mcp-Param-*` headers nothing consumes — and a malformed annotation
    would make conforming clients reject the whole tool. Everything else in the
    schema passes through untouched.

  ## Fixed

  * Resource references in a connector's prompt and tool results — `resource_link`
    blocks, embedded resources, and mentions of those same URIs in the result's
    text — are now rewritten to the gateway's `conduit://<connector>/<uri>` form,
    matching the resource listing. Previously they relayed the connector's own
    raw URIs, which a `resources/read` through Conduit could not resolve (so a
    prompt telling the model to load a resource pointed at a URI the gateway
    answered as not found). A read of a URI outside the wrapped form also now
    returns an error spelling out that form, so a model holding a raw URI quoted
    in prose alone can construct the routable form and retry.

  ## Security

  * The workspace join policy no longer returns the invite-link token. Reading
    the policy (`GetWorkspaceJoinPolicy`, including the `settings:read` API
    scope and the workspace-admin MCP tool) now reports only whether a link
    exists (`link_token_set`); the token itself — a live credential that admits
    any signed-in user to the workspace while link joining is enabled — moves to
    a new `GetWorkspaceJoinLink` call gated at `settings:write`, the same
    authority that can mint the link. Automation that read `link_token` from the
    policy must call `GetWorkspaceJoinLink` instead.

  * Upstream OAuth discovery now validates the `resource` identifier a
    connector's protected-resource metadata advertises (RFC 9728 §3.3): it must
    name the MCP server itself — the same origin, at the endpoint's path or a
    parent of it. Metadata naming a foreign resource fails the connect flow
    with a clear error instead of requesting tokens audienced for that other
    resource (a confused-deputy defense). Connectors whose metadata advertises
    a mismatched identifier will fail to connect until the upstream fixes it.

  * The MCP endpoint now validates the `Origin` header (DNS-rebinding defense,
    an MCP transport requirement): a browser-sent `http(s)` origin that isn't
    the instance's own external origin (`CONDUIT_BASE_URL`) is refused with
    403\. Requests without an `Origin` header — every non-browser MCP client —
    are unaffected.

  * The connector OAuth callback now validates the RFC 9207 `iss` parameter on
    authorization responses (mix-up defense): a present `iss` must match the
    issuer recorded when the connector's authorization server was discovered,
    and an authorization server whose metadata advertises
    `authorization_response_iss_parameter_supported` must send it.
    Authorization servers that predate RFC 9207 are unaffected, as are
    manually-configured OAuth clients (no discovered issuer to compare
    against). Includes a migration (runs automatically on boot); connectors
    configured before this release record their issuer on the next
    connector edit.

  [Compare v0.9.6...v0.10.0](https://github.com/PipedreamHQ/conduit/compare/v0.9.6...v0.10.0)
</Update>

<Update label="v0.9.6" description="2026-08-26">
  *Enforces the Content Security Policy by default, requires consented OAuth capability scopes, adds a per-workspace usage dashboard, and hardens sessions, CSRF, and request throttling.*

  ## Upgrade notes

  The new Content Security Policy is enforced by default. Deployments diagnosing
  a custom browser integration can temporarily set `CONDUIT_CSP=report-only`.

  OAuth scope storage is added automatically on startup. Existing client grants
  retain connector and personal tools only; users must reconnect and explicitly
  approve an administrative scope before an OAuth client can use admin tools.

  ## Added

  * The MCP endpoint and the `conduit` CLI's stdio server answer
    `server/discover`, the pre-negotiation probe clients on MCP protocol
    revision `2026-07-28` and later send before anything else. The response
    lists the supported protocol versions (the initialize-handshake revisions —
    Conduit does not yet implement the stateless `2026-07-28` semantics),
    capabilities, and server identity in one round trip, so a probing client
    selects a supported version and proceeds with the `initialize` handshake
    instead of inferring the fallback from a method-not-found error.

  * Workspace usage dashboard: workspace admins get **Settings → Usage**,
    showing their workspace's tool-call volume, active members, top
    users/tools/clients, and recent calls — the dashboard instance admins
    already had, scoped to one workspace. Backed by new workspace-admin RPCs
    (`GetOrgUsageOverview`, `ListOrgUsageBreakdown`, `ListOrgRecentToolCalls`)
    that never read outside the workspace: the per-member drill-down shows only
    calls made in that workspace, never a member's activity elsewhere. In
    single-workspace mode the instance and workspace dashboards fold into one
    page, as the audit log already does.

  ## Changed

  * CSP violation reports that say nothing about the deployment are no longer
    logged or emitted as telemetry events: content a browser extension injected
    into the page (which no policy can govern, and which browsers report anyway)
    and reports naming a page outside `CONDUIT_BASE_URL`, which anyone could
    submit to the unauthenticated report endpoint. A report that survives the
    filter is a real policy regression.

  * The **Groups & Policies** dialogs edit as a draft: the grant editor and the
    group-members dialog collect changes behind **Cancel**/**Save** and write
    them in one atomic call on Save, instead of persisting every toggle
    immediately — so an accidental click is discardable and a half-finished
    edit is never live policy. The grant editor's **Allow all connectors**
    switch still applies immediately. Saving membership sends only the explicit
    adds and removals, so a member added while the dialog was open — by a SCIM
    sync, a provisioning rule, or another admin — is never evicted by a save
    that didn't touch them. Backing this, the `update_group` admin tool
    (`UpdateGroup`) accepts a batch of membership changes alongside its
    now-optional rename, and the single-member `add_group_member` /
    `remove_group_member` tools (`AddGroupMember` / `RemoveGroupMember`) are
    removed — `update_group` covers both, and workspace-admin clients get a
    shorter tool list.

  * The usage dashboards' per-user drill-down is headed by who it is about —
    the user's avatar, name, and email — instead of a generic "User usage"
    title. On the workspace dashboard the identity comes from the member
    roster (`ListWorkspaceMembers` gains an exact `user_id` filter), so an
    ex-member's remaining history falls back to the generic heading.

  ## Fixed

  * The built-in docs site prefetches pages again. Its Content-Security-Policy
    blocked every link prefetch, so in-site navigation lost the head start the
    docs are built to have.

  ## Security

  * OAuth clients now receive explicit, consented capability scopes enforced on
    listings and direct calls in addition to the caller's live role and workspace
    policy; ordinary connector and personal-tool scopes are required, while admin
    scopes are opt-in and hidden from ineligible users.
  * Stateful MCP upstream sessions are isolated per member even when a connector
    uses shared or no authentication, preventing one member from receiving
    another member's upstream session state.
  * OpenAPI connector path parameters can no longer traverse outside the
    operation declared in the connector's specification.
  * Compressed Connect RPC requests are rejected before decompression and the RPC
    surface is throttled per client IP, preventing unauthenticated gzip bombs
    from exhausting server CPU.
  * The web UI now enforces its Content Security Policy by default, denies all
    framing even when CSP is disabled, and briefly holds OAuth approval input to
    prevent clickjacking.
  * MCP tool calls now reject nonsensically oversized names before tracing,
    routing, or recording them. High-volume call activity remains in bounded
    usage data and OpenTelemetry; policy denials remain in the audit log.
  * HTTPS deployments now use a `__Host-` browser-session cookie (with automatic
    migration of existing sessions) and send a one-year, host-only HSTS policy.
  * Cookie-authenticated Connect GETs now require the exact application origin;
    sibling subdomains and CORS-allowed origins no longer satisfy CSRF checks.
  * PostgreSQL deployments now atomically consume shared password-attempt
    counters across replicas, keyed separately by account and IP, so concurrent
    requests cannot race past the shared limit.
  * Connector create and update requests with a missing connector payload now
    return `InvalidArgument` instead of panicking.
  * MCP tool-list change detection now hashes complete advertised definitions,
    including behavior hints, plus a non-secret connector-source fingerprint, so
    clients are notified when a trusted name's definition or destination changes.

  [Compare v0.9.5...v0.9.6](https://github.com/PipedreamHQ/conduit/compare/v0.9.5...v0.9.6)
</Update>

<Update label="v0.9.5" description="2026-08-24">
  *The MCP gateway gains prompts, upstream resources, argument completion, client logging, and request cancellation, and reports connector failures as spec-compliant tool results.*

  ## Upgrade notes

  None.

  ## Added

  * MCP prompts: the gateway now aggregates prompts from connectors that serve
    them (`prompts/list` / `prompts/get`), named `<connector>__<prompt>` and
    gated by the same connector access policies as tools; connected clients get
    `notifications/prompts/list_changed` when their prompt set changes. The
    Conduit CLI proxies prompts through. Prompt fetches and resource reads are
    recorded in the usage store and metrics
    (`conduit_mcp_prompt_gets_total`, `conduit_mcp_resource_reads_total`), and
    denied fetches are audited like denied tool calls. Includes a usage-store
    migration (runs automatically on boot).
  * MCP resources from remote connectors: `resources/list`, `resources/read`,
    and `resources/templates/list` now aggregate what upstream MCP servers
    expose, under the same connector access policies as tools. An upstream
    resource is advertised as `conduit://<connector>/<original-uri>` so reads
    route to the owning connector by name; resource templates expand inside
    that form and round-trip. The Conduit CLI proxies resources through.
    New connector names are restricted to letters, digits, and `. _ ~ -` (they
    are the routing segment of resource URIs); a pre-existing name outside that
    set keeps working but serves no resources until renamed.
  * MCP argument completion: `completion/complete` for a prompt or resource
    template routes to the connector that owns it, under the referent's own
    access policy; connectors that offer no completion answer an empty
    suggestion list.
  * MCP logging: clients can opt into gateway log messages with
    `logging/setLevel` (persisted per session, honored by every replica).
    Conduit sends a warning when one of the caller's connectors fails during a
    listing — previously visible only in server logs — and forwards the log
    messages a connector itself emits while answering the caller's requests
    (`connector:<name>` logger prefix), which were previously dropped. Includes
    a database migration (runs automatically on boot).
  * MCP request cancellation: the standard `notifications/cancelled`
    notification now aborts the in-flight request it names, including the
    upstream connector call behind it. Behind multiple replicas this relies on
    the same sticky routing already required for in-band prompts.

  ## Changed

  * Published binaries (the server in the Docker image and the `conduit` CLI
    downloads it serves) are built stripped of debug info and without
    build-machine paths, making each roughly a third smaller. Panic stack
    traces and runtime profiling are unaffected.
  * The MCP endpoint reports the instance's real build version (and a display
    title) in the `initialize` response instead of a fixed placeholder, and
    identifies itself with that version to upstream connectors.
  * MCP list requests that carry a pagination cursor are rejected with an
    invalid-params error. Conduit returns every list in one page and never
    issues cursors, so a cursor can only be one this server did not mint;
    re-serving the full list would masquerade as pagination.

  ## Fixed

  * The workspace member roster no longer discloses each member's instance-level
    role to workspace admins who are not instance admins — who holds instance
    admin is instance-scoped information. Instance admins still see and manage
    it as before; the Members page shows the instance-role column only to them.
  * The login page now shows the email and password fields whenever the server
    would accept a password sign-in — including break-glass recovery
    (`CONDUIT_ADMIN_EMAIL` + `CONDUIT_ADMIN_PASSWORD`) on instances that have
    disabled password sign-in by policy. Previously the fields were hidden with
    password sign-in disabled, leaving the accepted break-glass credential with
    no form to be entered into. In that state the instance Sign-in settings now
    also show a warning that break-glass is keeping password sign-in active,
    recommending a restart without the variables as soon as possible.
  * Upstream MCP servers that answer a request over SSE with notification
    frames ahead of the result (progress or log messages) are now parsed
    correctly — the response frame is selected instead of the first frame.
  * Tool calls that fail in the connector behind them (an unreachable server, an
    HTTP error, a response over the gateway's 8 MiB cap, a timeout) are now
    reported as tool results with `isError: true`, as the MCP spec requires,
    instead of JSON-RPC protocol errors. The failure text reaches the model, and
    when one of the gateway's own limits stopped the call it now explains how to
    adjust (request less data, or less work per call) — previously these
    failures looked transient to clients, inviting identical retries that could
    never succeed.

  [Compare v0.9.4...v0.9.5](https://github.com/PipedreamHQ/conduit/compare/v0.9.4...v0.9.5)
</Update>

<Update label="v0.9.4" description="2026-08-20">
  *Reconnecting an app account now updates it in place, and each user keeps a single connected account per app — accumulated duplicates clean themselves up on the next connect.*

  ## Upgrade notes

  None.

  ## Fixed

  * **Reconnecting an app account no longer creates a duplicate, and each app
    now keeps a single connected account per user.** Reconnect updates the
    existing account's credentials in place, and completing a connect flow
    removes any other accounts you had for that app, keeping the one just
    connected — tool calls don't yet offer a way to choose between multiple
    accounts for the same app, so keeping one removes the ambiguity about which
    account a call uses. Duplicates that accumulated earlier are cleaned up the
    next time the affected user connects or reconnects that app; each removal
    is recorded in the audit log.

  [Compare v0.9.3...v0.9.4](https://github.com/PipedreamHQ/conduit/compare/v0.9.3...v0.9.4)
</Update>

<Update label="v0.9.3" description="2026-08-19">
  *A personal Usage page for every user, plus a security fix preventing cross-tenant workspace joins via a workspace-scoped identity provider.*

  ## Upgrade notes

  No action required. A usage-database migration builds three new `tool_calls`
  indexes on first boot; on an instance with a long call history that adds a
  one-time startup delay.

  ## Added

  * **A personal Usage page.** Every user now has a Usage page (`/usage`) showing
    their own tool-call activity — time range, workspace filter, daily chart, top
    connectors/tools, connected clients, and recent calls. The home-page summary
    cards link to it.
  * New documentation: Single Sign-On, Access Control (roles, groups, and
    policies), Connectors, the Pipedream Connector, How MCP Authorization
    Works, and Security Hardening — plus a deployment validation checklist on
    the Kubernetes page.

  ## Fixed

  * **Recreating a group, or re-inviting a removed member, with the same name
    could fail with "policy already exists."** Deleting a group or removing a
    member left its connector-access policy behind with no effect, and that
    leftover permanently squatted on the name; it's now cleaned up along with
    the group or membership, and any such leftovers from earlier deletions are
    cleaned up automatically on upgrade.

  ## Security

  * **A workspace's own identity provider could open the door to another
    workspace.** On a multi-workspace instance, an email address asserted by a
    workspace-scoped IdP was treated as verified for any domain — so a tenant's
    IdP could mint a verified address in a domain another tenant owns and join
    that workspace through its allowed-domain join policy. An address from a
    workspace-scoped IdP now counts as verified only for a domain the IdP has
    proven via the DNS TXT challenge (instance-scoped IdPs are unaffected), and
    a migration on first boot clears the verified flag on accounts whose only
    basis was an unproven workspace IdP.

  [Compare v0.9.2...v0.9.3](https://github.com/PipedreamHQ/conduit/compare/v0.9.2...v0.9.3)
</Update>

<Update label="v0.9.2" description="2026-08-14">
  *Identity provider sign-in scopes are now editable, sign-in audit events record provisioning-rule results, and workspace provisioning rules now apply on a user's first SSO sign-in in single-workspace mode.*

  ## Upgrade notes

  None.

  ## Added

  * **Editable scopes on identity providers.** The scopes requested from an
    identity provider at sign-in are now editable in both the instance sign-in
    settings and workspace SSO settings, prefilled with the defaults
    (previously they were fixed at `openid email profile`). Add whatever scope
    your IdP requires to include extra claims in the ID token — for example
    Okta requires the `groups` scope for a groups claim, which user
    provisioning rules can then match on.

  * **Sign-in events now record provisioning-rule results.** Each successful
    sign-in's audit event lists which user provisioning rules matched and, for
    SSO sign-ins, the names of the claims the identity provider asserted
    (names only, never values). When a rule doesn't fire, the audit log now
    shows whether the claim it depends on ever arrived. For deeper
    troubleshooting, running the server at debug log level also logs each SSO
    sign-in's full verified ID-token claims.

  ## Fixed

  * **Workspace provisioning rules now apply on the first sign-in in
    single-workspace mode.** A workspace-scoped user provisioning rule (for
    example "SSO claim `groups` contains X → add to group Y") used to be
    skipped on a user's very first SSO sign-in when the instance runs in
    single-workspace mode with a deployment-wide identity provider — the rule
    only took effect from their second sign-in. The user's imminent membership
    in the sole workspace now counts during rule evaluation, matching how
    password sign-in already behaved.

  [Compare v0.9.1...v0.9.2](https://github.com/PipedreamHQ/conduit/compare/v0.9.1...v0.9.2)
</Update>

<Update label="v0.9.1" description="2026-08-13">
  *Connectors backed by stateful MCP servers — including servers hosted on AWS Bedrock AgentCore — now work: the gateway keeps the server's MCP session across requests instead of being rejected after the handshake.*

  ## Upgrade notes

  None.

  ## Fixed

  * **Stateful MCP servers now work as connectors.** The gateway keeps the
    session a connector's MCP server establishes during the initialize
    handshake (its `Mcp-Session-Id`) and presents it on every subsequent
    request, starting a fresh session automatically if the server expires it.
    Servers that require this — including those built on the official MCP
    TypeScript SDK's default stateful mode, and MCP servers hosted on AWS
    Bedrock AgentCore, which uses the session id to route requests — previously
    rejected every request after the handshake with a "session not found"
    error. Servers that don't use sessions are unaffected. For connectors
    authenticated per user, each user gets their own upstream session.

  [Compare v0.9.0...v0.9.1](https://github.com/PipedreamHQ/conduit/compare/v0.9.0...v0.9.1)
</Update>

<Update label="v0.9.0" description="2026-08-13">
  *Every instance now distributes the conduit CLI — device-code login, auto-configured AI clients, and local MCP connectors governed by your workspace policies — plus IAM authentication to AWS RDS; stop the old container before starting this version.*

  ## Upgrade notes

  **Stop the old container before starting the new one.** This release's
  migrations rename a connector column and drop another, so a replica running the
  previous version against the same database will fail its connector reads and
  writes until it is replaced. A normal stop/start (or a Kubernetes `Recreate`
  rollout) is unaffected; a rolling upgrade that runs both versions at once is
  not supported for this release. No multi-replica deployment configuration is
  affected today, since Postgres multi-replica mode is not yet in use.

  Otherwise no operator action: the migrations run automatically on boot, and the
  Docker image now also carries the `conduit` CLI binaries it serves.

  ## Added

  * **The `conduit` CLI: your instance's tools, from a laptop.** Every Conduit
    instance now distributes its own command-line client at `/cli/` — users
    install it with one `curl | sh` from the home page, sign in with
    `conduit login` (a device code approved in the browser, no copy-pasting
    credentials), and are done. The install script, the binaries, and their
    checksums all come from your instance, so air-gapped deployments need
    nothing external and the client always matches the server it talks to.

  * **`conduit setup`: your AI clients wired up for you.** The install script
    ends by detecting the AI clients on the machine — Claude Code, Claude
    Desktop, Cursor, Windsurf, VS Code, Codex CLI, and Gemini CLI — and offering
    to register Conduit's MCP server with each one: a picker with everything
    preselected, and idempotent config edits that leave other servers and
    unrelated settings untouched (one caveat: an edit to Codex's `config.toml`
    re-encodes it, so comments there don't survive — a file that's already
    current is never rewritten). A client already pointing at the remote `/mcp`
    URL under the `conduit` name is switched to the CLI — it proxies the same
    remote tools, so keeping both entries would duplicate them. Run
    `conduit setup` again any time (say, after installing a new client), or
    `conduit setup --all` where there is no terminal to ask on.

  * **Local connectors: MCP servers that run on the user's own machine, under
    your policies.** Admins can define a connector by launch command
    (**Add connector → MCP Server by command**) instead of by URL. Users enable
    it on their Connectors page like any other, and the CLI runs it locally:
    every tool call is authorized by your instance first, counts in usage, and
    lands in the audit trail exactly like a remote call — but a successful tool's
    results stay on the user's machine and never transit the server (only the
    outcome, and a failed call's error message, are reported back). Access is
    governed by the same allow/enable policies as everything else, including as a
    class (`local:*`). Launch commands are found using the PATH the user's login
    shell sets up, so a runtime installed with Homebrew or nvm works even when
    the MCP client wasn't started from a terminal.

  * **Environment variables for local connectors, secret-safe.** A local
    connector's env vars are declared the same way a remote connector's custom
    headers are — the field editor labels the key as an environment variable or a
    header depending on the connector. A value can be a shared workspace secret or
    one each member supplies on their connect page; either way it is encrypted at
    rest and never shown back to you in the UI or API after you save it.

    One thing to know when choosing the scope: a local connector runs on the
    member's own machine, so its environment has to be sent there. A
    **workspace** value is therefore readable by every member who can use that
    connector — treat it as shared with them — while a **per-member** value stays
    that member's own and is never handed to anyone else's machine. The field
    editor says so where you enter the value. This applies only to local
    connectors; a remote connector's workspace values are used server-side and
    never leave the instance.

  * **`conduit mcp`: one local endpoint for everything.** Pointing an MCP client
    (Claude Code, Claude Desktop, Cursor, Codex) at `conduit mcp` gives it the
    instance's remote tools *and* the machine's local connectors in a single
    tool list. The home page's connect card now leads with this path, with
    per-client setup instructions and an npx quick-add.

  * **Live updates reach CLI sessions.** When an admin changes a policy or a
    connector — or a user changes their own enabled set — connected
    `conduit mcp` sessions pick it up immediately: the tool list refreshes in
    the client, and local connectors start or stop mid-session to match. That
    includes editing a local connector's launch command, arguments or
    environment variables: running sessions retire the old process and use the
    new definition on the next call, so a fix reaches every machine without
    asking anyone to restart their MCP client. No restarts.

  * **`conduit upgrade`: the fleet converges on your server's version.** The CLI
    compares its own binary against what the server distributes (by content
    digest, so a rebuilt image with the same tag still converges — and a server
    rollback rolls the fleet back the same way), verifies the download's
    checksum, and swaps itself atomically. `conduit login` and `conduit mcp`
    print a notice when out of sync; `conduit upgrade --check` exits non-zero
    for scripts. A running `conduit mcp` session adopts the new binary in place
    — same process, same client connection, no restart of the MCP client — and
    a failed upgrade can never take a session down.

  * **Fleet visibility for the CLI, in the audit log and telemetry.** New audit
    events record each CLI connection (with the user, workspace, and CLI
    version) and every local connector that fails to start or crashes — with a
    machine-readable error class (for example, "the runtime isn't installed")
    and the failing process's own error output, so you can see whether a rollout
    works on real machines without asking anyone. Denied local tool calls are
    audited exactly like denied remote ones, successful starts and binary
    downloads flow to telemetry, and every CLI request carries its version, so
    stale clients are identifiable. When a failure has a known fix, the server's
    guidance is shown to the user in their MCP client, and the connector fails
    fast until the definition changes — no retry storms, no repeated log spam.

  * **`conduit --version` and `conduit --help`** (with `-v`/`-h` shorthands)
    report the build and usage; `conduit login -h` and friends exit cleanly.

  * **IAM authentication to AWS RDS and Aurora.** Set
    `CONDUIT_DATABASE_AUTH=rds-iam` and Conduit authenticates to its PostgreSQL
    with short-lived IAM tokens minted from the AWS identity it runs as (an EKS
    service-account role, Pod Identity, or an instance profile) instead of a
    password in `CONDUIT_DATABASE_URL` — no database credential stored anywhere,
    nothing to rotate. Setup steps are on the Deployment Tiers page; the
    Kubernetes page shows the EKS wiring. To support mounting the RDS CA bundle
    (IAM auth requires TLS), the Helm chart gained `extraVolumes` /
    `extraVolumeMounts`.

  ## Changed

  * **Connectors can no longer be created or edited through the builtin MCP
    tools.** The `create_connector` and `update_connector` tools are gone from
    the `conduit_workspace_admin` builtin connector: their inputs carry upstream
    credentials (tokens, auth headers, client secrets), which don't belong in
    tool-call transcripts. Use the Connectors settings page instead; listing and
    deleting connectors remain available as builtin tools.

  * **The home page's "How to use Conduit" card offers two paths.** "All tools"
    (recommended) walks through installing the CLI and pointing clients at it;
    "Remote tools only" keeps the zero-install hosted URL flow for clients that
    can't run local processes (like ChatGPT).

  * **Connector "header fields" are "config fields" in the API.** The declared
    field list on a connector covers two transports — an HTTP header on a remote
    connector, an environment variable on a local one — so the API now names it
    for the concept: `header_fields` on connector reads/writes is
    `config_fields` (message `ConnectorConfigField`), and the connect page's
    `user_header_fields` / `user_header_keys_set` are `user_config_fields` /
    `user_config_keys_set`. Scripts that call the connectors API with the old
    field names need updating; stored declarations and values are preserved (the
    database migrates automatically on boot) and the UI is unchanged.

  * **`stdio.env` is gone from the connector API.** A local connector's
    environment is declared as config fields now, so the values are encrypted at
    rest and write-only instead of stored in the clear. A script still sending
    `stdio.env` has it ignored rather than rejected — move those entries to
    `config_fields` (scope `workspace` or `user`) and supply the values the same
    way you would a custom header's.

  * **A CLI login is only issued to the Conduit CLI.** The device-authorization
    endpoint now accepts only the first-party `conduit-cli` client. The approval
    screen names the client asking for access, and that name is what tells you
    whether a login is one you started — so it has to be a name the server
    vouches for rather than whatever a caller sent.

  * **The CLI requires HTTPS.** `conduit login`, `conduit upgrade`, and
    `conduit mcp` refuse a plain-`http` instance URL unless it is localhost.
    `conduit upgrade` replaces the running binary with one downloaded from your
    instance and checks it against a checksum from that same instance, so the TLS
    connection is what makes the check meaningful.

  [Compare v0.8.1...v0.9.0](https://github.com/PipedreamHQ/conduit/compare/v0.8.1...v0.9.0)
</Update>

<Update label="v0.8.1" description="2026-08-10">
  *Custom MCP servers gain the OAuth client-credentials grant, and OAuth discovery now follows the metadata URL a server advertises in its 401 challenge.*

  ## Upgrade notes

  None.

  ## Added

  * Custom MCP servers can now authenticate upstream with the **OAuth client
    credentials grant**, matching OpenAPI and GraphQL connectors. Pick "Client
    credentials" as the grant type when adding or editing an MCP connector and
    supply the provider's client ID, client secret, and token endpoint (plus
    optional scopes and audience) — Conduit mints a workspace-wide token itself
    and refreshes it before expiry, so members never connect their own accounts.
    Switching an existing MCP connector between grants revokes credentials issued
    by the client being replaced, and the editor warns before saving.

  ## Changed

  * Adding an OAuth MCP connector now discovers the server's OAuth configuration
    from the metadata URL the server itself advertises when it rejects an
    unauthenticated request, so servers that don't publish metadata at the
    standard well-known paths — such as agents hosted on Amazon Bedrock
    AgentCore — can now be added.

  [Compare v0.8.0...v0.8.1](https://github.com/PipedreamHQ/conduit/compare/v0.8.0...v0.8.1)
</Update>

<Update label="v0.8.0" description="2026-08-06">
  *Connectors are now opt-in per member, workspace deletion removes everything it owned, and the container runs as a non-root user — read the upgrade notes before pulling.*

  ## Upgrade notes

  * **The container now runs as a non-root user** (UID 65532). Kubernetes
    installs using the Helm chart need no action — the chart's new default
    security context re-owns the data volume on mount. Raw-manifest installs
    should add the `securityContext` blocks now shown on the
    [Kubernetes page](/docs/conduit/deploy/kubernetes). Docker installs upgrading an
    existing data volume must re-own it once before starting the new version
    (a fresh install needs nothing):

    ```sh theme={null}
    docker run --rm -v conduit-data:/data alpine chown -R 65532:65532 /data
    ```

  * The database is migrated automatically on boot, on both SQLite and
    Postgres. Rows stranded by deletions made under earlier versions — access
    policies, groups, provisioning rules, connectors and their stored
    credentials belonging to workspaces or users that no longer exist — are
    removed by the migration. They were unreachable, but if you want a record
    of them, take your usual pre-upgrade backup first.

  * Sign-ins already in progress complete normally when you restart into this
    version. Rolling back afterwards is the case to know about: a sign-in started
    by this version and finished by the previous one is rejected by your identity
    provider, because the challenge it was started with can no longer be proved.
    Nothing to do — the pending records expire within five minutes, and a retry
    succeeds.

  * If you had switched off any built-in Conduit tool connector with the
    `update_builtin_tools` admin tool, that switch no longer exists: after this
    upgrade, built-in tools follow workspace access policies. Withhold them in
    each workspace's policies to keep them off.

  * **Members who never chose their connectors now start with built-in tools
    only.** Previously a member who had never touched the **Connectors** page was
    served everything their workspace allowed them — every connector that
    authenticates at the workspace level, and every Pipedream app named in their
    policies. After this upgrade those are switched off until the member switches
    them on, one connector at a time, on the Connectors page. Members who have
    already made a selection are unaffected, and nothing changes about what a
    policy allows: this is only the starting point of a member's own selection.
    Tell affected members to visit Connectors after the upgrade — or let their
    agent do it, since Conduit's own `get_my_connectors` and
    `set_connector_enabled` tools stay available and can enable a connector from
    the client.

  ## Changed

  * **A connector is off until the person using it turns it on.** A member's
    selection on the **Connectors** page starts as Conduit's built-in tools and
    nothing else; every other connector their workspace allows them — an MCP
    server, an OpenAPI or GraphQL connector, a Pipedream app — waits there for
    them to switch on. Allowing a connector in a policy is what makes it
    available to a member, not what puts it in their client, so a workspace-wide
    grant no longer lands dozens of tools in everyone's client on its own. See
    the Upgrade notes for what this means for existing members.

  * **A connection blocked by outbound network protection now explains the fix.**
    Saving Pipedream credentials, saving an OAuth connector, or opening a
    connector's tools when the upstream's hostname resolves to a private address
    — reached through a private endpoint or gateway such as AWS PrivateLink, or
    on your own network — used to fail with only `blocked: connection to
    special-use address …`. The error now says to list the hostname in
    `CONDUIT_SAFEHTTP_ALLOWED_PRIVATE_HOSTS` and points to the
    [Network Access](/docs/conduit/deploy/network) page, which gains worked examples
    for Pipedream behind a private endpoint and for MCP servers on a private
    network.

  * **Deleting a workspace now deletes everything it owns.** Its groups, access
    policies, provisioning rules, identity providers and their SCIM tokens, its
    connectors — including stored credentials and connected accounts — and its
    Pipedream and telemetry settings are all removed with it. Previously these
    were left behind: invisible and unusable, but present, which for connector
    credentials meant secrets outliving the workspace they belonged to.
    Deleting a user account likewise now removes their remaining group
    memberships and OAuth consent records along with their sessions and
    connected accounts. These relationships are now enforced by the database
    itself, so nothing can strand them again.

  * **Built-in tool availability is decided by roles and access policies alone.**
    The instance-wide switches that could hide the built-in Conduit tool
    connectors — reachable only through the `update_builtin_tools` admin tool,
    never the UI — are removed, along with that tool. A built-in connector you
    want to withhold from a workspace comes out of its access policies, like any
    other connector. The stored switch state is dropped by a migration that runs
    automatically on boot.

  * **The Connectors page opens on an overview.** Bare `/connectors` no longer
    auto-selects a connector; the pane summarizes what you have enabled — tool
    counts ("4 of 27 tools"), connected state, and an inline Connect button for
    Pipedream apps. Clicking the page background (or pressing Escape) returns to
    it.

  * **The Connectors overview costs one request, not one per connector.** It
    used to fetch each enabled connector's tool list separately, so opening the
    page with 30 connectors enabled meant 30 concurrent upstream `tools/list`
    handshakes. Conduit now gathers them in a single call, fans out to the
    upstreams itself a few at a time, and skips connectors it knows you haven't
    connected yet — so a connector that can't be reached costs its own row rather
    than the whole summary, and the header total says what it covers ("120 tools
    enabled across 4 of 5 connectors") instead of disappearing. A connector that
    never answers costs the summary a few seconds rather than the request: the
    page comes back with that row uncounted.

  * **Connectors page polish.** The rail's sections collapse (Built-in starts
    folded; searching reveals everything), the connect-account control leads the
    tools panel header, and the search and tool-filter boxes carry an inline ✕ to
    clear them.

  * **Connector setup shows the redirect URI to register with your OAuth
    provider.** Adding or editing a connector that authenticates with an OAuth
    client you supply yourself — an MCP server that doesn't support dynamic client
    registration, or a GraphQL/OpenAPI connector — now displays Conduit's callback
    URL next to the client ID and secret, with a copy button. Previously admins had
    to assemble it from the instance's host and found out they had it wrong when a
    member's connection attempt came back as a `redirect_uri_mismatch`.

  * **Connector auth setup no longer offers "let members use their own credential
    instead" for OAuth.** OAuth members already connect their own accounts, so
    the setting was misleading. Existing connector behavior is unchanged, and
    API updates that omit `allow_user_override` preserve its stored value.

  * **Connector wizard polish.** The name is editable when importing a
    server.json (prefill, not a lock), and the details step's name field explains
    what the name is for (it prefixes the connector's tools). Correcting the pasted
    source re-derives what the later steps prefilled from it (anything you
    edited sticks), a step that outgrows the screen scrolls instead of pushing
    the buttons off it, and a header field's example label now comes from its
    header name.

  * **Single-workspace deployments no longer show a workspace filter on the
    Usage page.**

  * **1Password's inline suggestions stay out of non-credential fields.** Search
    boxes, filters, and config/secret fields no longer get the overlay; sign-in
    and setup keep it — those hold a user's own credential.

  ## Security

  * **The server runs as a non-root user.** The container image now runs as a
    fixed unprivileged user (UID 65532), and the Helm chart's default security
    contexts pin that for admission policies to verify: `runAsNonRoot`, the
    runtime's default seccomp profile, no privilege escalation, and every Linux
    capability dropped. The server never needed any of them. See the upgrade
    note above for existing Docker data volumes.

  * **Dependency security updates.** The server now builds with Go 1.26.5 and
    gRPC v1.82.1, picking up upstream fixes for a standard-library
    symlink-following issue and a vulnerability reachable through telemetry
    export (GO-2026-6061). The docs-site build tooling moves past vulnerable
    sharp, postcss and nanoid releases. Nothing to configure.

  * **Sign-in through an identity provider uses PKCE.** The authorization request
    now carries a one-time challenge that Conduit proves when it redeems the
    code, so a sign-in code captured on its way back — from browser history, a
    proxy log, a referrer — can no longer be redeemed at all.

    Conduit already rejected a sign-in built on an injected code, by checking the
    nonce in the identity token against the one it issued, so nobody could take
    over an account this way before. What changes is *where* the injected code
    dies: at your provider, instead of at Conduit after the exchange has already
    happened. That matters because the exchange is the step that hands Conduit
    the victim's tokens from your provider — today they are discarded, and this
    removes the possibility of ever obtaining them. It also brings the sign-in
    leg in line with OAuth 2.1 and with connecting a connector, which has always
    used PKCE.

    Nothing to configure, and no compatibility risk either way. A provider that
    does not implement PKCE ignores the extra parameters, as the OAuth spec
    requires. A provider that implements it and says so, but supports only the
    older `plain` transformation, is detected during discovery and is sent no
    challenge at all — sending one would make it refuse the sign-in. A provider
    whose app registration *requires* PKCE now works where it previously could
    not.

  ## Fixed

  * **Copy buttons draw their icon at full size.** The copy control beside a
    copyable value — the SCIM base URL, a new SCIM token, a connector's redirect
    URI — rendered at two-thirds scale inside its button.

  * **Connector tool counts refresh after access and connection changes.** The
    Connectors overview and individual tool panels could keep their previous
    results for up to five minutes after an access-policy edit, a Pipedream
    configuration change, or disconnecting a personal MCP credential.

  * **The workspace Provisioning page requires an active workspace.** An
    instance admin whose session had no active workspace saw every workspace's
    provisioning rules there, and a rule created in that state was reachable
    from no workspace at all afterwards. Both are now refused with an error
    naming the problem.

  * **Opening a connector that isn't serving tools says so.** Selecting one in a
    policy's connector list — which shows every connector in the workspace,
    including disabled ones, because a policy can grant any of them — reported a
    generic server error. It now explains that the connector is disabled or its
    configuration didn't load, and points to connector settings.

  * **A failed request logs what went wrong.** An operation that fails with an
    internal error now records the failure — which operation, and the error itself
    — in the server log and in the trace it exports. That covers both an API
    request and one of Conduit's own tools called over MCP, whose failures were
    previously reported only to the calling client. Nothing to configure; this
    changes what is recorded when a request fails, not when one fails.

  * **A server error now says which operation failed.** Where the UI used to show
    a bare "internal" on a failed save or list, it names the operation — "create
    connector failed", "list groups failed". The underlying cause goes to the
    server log and the trace, where an operator can see it, and stays out of the
    response: error text from the database or an upstream describes Conduit's
    internals, not anything the person clicking can act on.

  * **A momentary database failure no longer empties a workspace's connectors.**
    If the read that loads a workspace's connectors failed, the empty result was
    cached: for as long as the cache held, that workspace's tools were missing
    from `tools/list` and its connector pages showed errors. The failure is now
    logged and the next request retries.

  [Compare v0.7.0...v0.8.0](https://github.com/PipedreamHQ/conduit/compare/v0.7.0...v0.8.0)
</Update>

<Update label="v0.7.0" description="2026-08-03">
  *Connectors can require custom HTTP headers, with each value supplied once for the workspace or by each member on their own connect page.*

  ## Upgrade notes

  * **`search` and `fetch` are no longer offered to every client.** They are now
    served only to the clients that require them — ChatGPT connectors — and are
    hidden from everything else. If you were using them from another client to
    find a tool without reading the whole list, use the client's own tool list
    instead: it already contains every tool `search` could return.

  * **A spec whose server is plain `http` on a non-loopback host is now refused**
    when saving an OpenAPI connector that has no URL of its own — the same rule a
    typed URL has always followed. Existing connectors keep working until someone
    edits them; re-saving one records its destination and restores the two
    protections named under Fixed. `http://localhost` still works for local
    development with `CONDUIT_SAFEHTTP_ALLOW_LOCALHOST=1`.

  ## Added

  * **Custom header fields on connectors.** A custom MCP, GraphQL, or OpenAPI
    connector can now declare the extra HTTP headers its upstream requires —
    each with a human-friendly label, a description, and a required/secret
    flag — alongside whatever authentication it already uses. Every field is
    either **workspace-scoped** (the admin enters one shared value in the
    connector editor) or **member-scoped** (each member enters their own on the
    connector's connect page, exactly where per-user API tokens are entered
    today). This covers internal MCP servers that expect a per-user credential
    in a non-standard header (say `X-Jira-Token`) instead of
    `Authorization: Bearer`.

    Adding a connector from a pasted config picks the headers up for you. The
    wizard now accepts both the registry `server.json` (whose `remotes[].headers`
    carry a description and required/secret flags) and a client `mcpServers`
    entry — the shape an internal server's setup instructions usually show —
    and turns declared headers into fields, preselecting the **Custom**
    authentication method. Each field's value source stays unset until you pick
    it, since nothing in a config file says whether a value is shared or
    personal, and pasted values are never imported: a manifest's is a
    placeholder, and a real token belongs to the member who owns it.

    The `none` authentication method is now labelled **Custom** rather than "No
    authentication" — a server reached with admin-declared headers *is*
    authenticated, by those headers.

    A member-facing field description accepts a restricted subset of markdown —
    emphasis, inline code, lists and line breaks — so setup steps read as
    instructions rather than one long line, and the connector editor previews
    exactly what members will see. Links are allowed but can never hide where
    they go: the target host is displayed beside the link text, and only
    absolute `https` links are linked at all. Images are not rendered, and
    headings, quotes and tables are flattened to plain text, so admin-authored
    copy on a credential page cannot impersonate Conduit's own interface.

    Values are write-only in the API and encrypted at rest, responses only ever
    name the stored keys, and the connect page shows the exact host a value is
    sent to before the member types it. Changing a connector's URL revokes
    stored header values in both scopes — like every other stored upstream
    credential, a value is bound to the destination it was supplied for. A tool
    call that lacks a required member value answers with the connector's
    connect link, the same flow OAuth and token connectors use.

  ## Changed

  * **`search` and `fetch` are scoped to the clients that need them.** The two
    tools exist because a ChatGPT connector outside developer mode accepts nothing
    else: a server that exposes them is declaring itself a searchable document
    corpus, and Conduit answered with its own tool catalog. Advertised to every
    client, that misled the capable ones. A model reads a tool named `search` as a
    knowledge source to ground an answer in, so it would spend a turn searching —
    and get back tool schemas rather than an answer — then treat the returned
    schemas as an instruction to start calling those tools. It also read as
    evidence of tools the client could not see, when there were none: every tool
    `search` can return is already in the client's tool list.

    Conduit now identifies the client from the name it reports when it connects
    and offers the pair only to ChatGPT, which needs it. Every other client gets
    the plain tool list. ChatGPT connectors are unaffected.

  ## Fixed

  * **An OpenAPI connector's destination is now recorded where the rest of Conduit
    looks for it.** Such a connector can be registered with a spec and no URL, in
    which case requests went to the base URL declared inside the spec. Two
    protections read the URL field instead: the host shown to a member before they
    enter a credential, and the rule that revokes stored credentials when a
    connector is pointed somewhere new. For those connectors the first showed
    nothing and the second never fired, so editing only the spec could redirect
    every member's stored credential to another host silently. The base declared
    by a spec is now resolved into the connector's URL when it is saved, which
    also means it passes the same https and address checks a URL you type has
    always passed. A connector that asks each member for a value is refused
    without a URL, since there would be no destination to show them.

  [Compare v0.6.1...v0.7.0](https://github.com/PipedreamHQ/conduit/compare/v0.6.1...v0.7.0)
</Update>

<Update label="v0.6.1" description="2026-07-31">
  *Tool search finds a tool from a plain phrase again, and the identity-provider sign-in state token is stored hashed.*

  ## Upgrade notes

  * A sign-in already in progress when you restart into this version fails once
    and succeeds on retry: the pending flow was recorded in the previous format
    and cannot be completed by the new one. Nothing to do — the stale records
    expire within five minutes and are cleaned up on their own.

  ## Security

  * **The sign-in state token is stored hashed.** The short-lived token that ties
    an identity-provider redirect to its callback was the last opaque credential
    Conduit wrote to the database in readable form. It is now hashed like every
    other one — sessions, refresh tokens, authorization codes — so a copy of the
    database cannot be used to claim a sign-in that is still in flight.

  ## Fixed

  * **Tool search understands a phrase.** The `search` tool that ChatGPT
    connectors use — and that any client can use to find a tool without reading
    the whole list — required every word of the query to appear in a tool's text,
    so a natural request like "list my google calendar events" returned nothing
    at all because no tool's text contains "my". It now ranks tools by how much
    of the query they match and answers with the closest first, so a word that
    matches nothing is ignored instead of excluding everything. Searching by app
    or connector name works again too: the shorter tool names introduced in
    v0.6.0 left search matching only the abbreviated form, so `google_sheets`
    found nothing where the wire name says `pd__gsheets-`. `google_sheets`,
    `google calendar` and `google-calendar` all find their tools now.

  [Compare v0.6.0...v0.6.1](https://github.com/PipedreamHQ/conduit/compare/v0.6.0...v0.6.1)
</Update>

<Update label="v0.6.0" description="2026-07-31">
  *Prometheus metrics, traces for every outbound call and SQL statement, shorter MCP tool names, and a Helm chart for Kubernetes.*

  ## Upgrade notes

  * Built-in and Pipedream tools are advertised under new, shorter names. A
    client that reads the tool list each session picks them up on its own, and
    the previous longer names are still accepted on a tool call — so a saved
    agent configuration keeps working.
  * The exception is the nine identity-provider tools, which were also
    **renamed** (`create_workspace_identity_provider` →
    `create_workspace_idp`, and likewise for create/update/delete/list at both
    the instance and workspace level, plus
    `verify_workspace_identity_provider_domain` →
    `verify_workspace_idp_domain`). A configuration naming one of those by hand
    needs updating.
  * Access policies need no change — they are written against the stored name,
    which has not moved.

  ## Added

  * **Prometheus metrics.** Set `CONDUIT_METRICS_ADDR` (Helm:
    `metrics.enabled`) to expose a `/metrics` endpoint on a dedicated port —
    tool-call rates and latencies per connector, MCP auth failures, connected
    SSE sessions, HTTP request durations, database pool health, and Go runtime
    metrics. The same metrics are pushed over OTLP to the OpenTelemetry
    exporters configured in Settings → Telemetry, next to the existing traces
    and events. See [Monitoring](/docs/conduit/deploy/monitoring).

  * **Telemetry for over-long tool names.** A new OTel event,
    `conduit.mcp.tool_name.over_budget`, tells you when Conduit advertised a tool
    name long enough that a client may reject or truncate it. It fires once per
    `tools/list` carrying a name of 50 bytes or more — 64 (the limit the
    strictest AI clients enforce) minus the 14 a client spends prefixing the name
    itself as `mcp__conduit__` — and reports how many names were over, how many
    tools were listed, and the longest one, which identifies the connector
    responsible. Look for it in your OTLP backend, alongside the other
    `conduit.mcp.*` events. The most effective fix is usually on the client side:
    registering this endpoint under a short name (`conduit`) leaves far more room
    for the tool name than a long one does.

  * **Helm chart.** Conduit now ships a Helm chart for Kubernetes:
    `helm install conduit oci://ghcr.io/pipedreamhq/charts/conduit --version X.Y.Z`.
    One chart covers both deployment tiers — configuring PostgreSQL
    (`database.secretKey`) switches from embedded SQLite on a persistent
    volume to stateless rolling updates with `replicas > 1` — and the chart
    enforces the tier rules (single replica on the embedded tier, an existing
    Secret for the encryption key, `baseURL` derived from the ingress host).
    Each chart release pins its matching image. See
    [Deploy on Kubernetes](/docs/conduit/deploy/kubernetes).

  * **Private registries and source builds.** The Kubernetes guide covers
    mirroring the image and chart into your own registry, and the Docker build
    now accepts build args (`GOPROXY`, `NPM_CONFIG_REGISTRY`, `ALPINE_MIRROR`,
    and base-image overrides) so the image can be built entirely against a
    private package registry — see
    [Build from Source](/docs/conduit/deploy/build-from-source).

  ## Changed

  * **Traces now cover every outbound call, and an unreachable service raises an
    alert.** Conduit traced the requests it makes to your MCP connectors, but calls
    to other services showed up as an unexplained gap in the trace — the token
    exchange with your identity provider during a sign-in, requests to the
    Pipedream API, sending an email. Each of those is now its own timed span in
    your OTLP backend (`POST api.pipedream.com`, and so on), recording the method,
    the host it called, and the response status. So a slow sign-in or a connector
    list that won't load can be traced to the service responsible instead of
    inferred.

    A service Conduit cannot reach — DNS failure, connection refused, TLS failure,
    a timeout — is now also reported as an error to your tracing backend, grouped
    by what failed (`conduit.pipedream.api_failed`,
    `conduit.auth.idp_request_failed`, `conduit.email.send_failed`, and so on), so
    it can raise an alert rather than sitting unnoticed in a trace nobody opens.
    Requests that Conduit's own SSRF hardening refused are deliberately excluded:
    that is the guard working as intended, not an outage.

    Request paths and query strings are deliberately **not** recorded, because for
    an OpenAPI connector they carry the arguments of the tool call — the same
    reason Conduit already redacts tool call parameters from traces.

  * **Database queries appear in traces.** Every SQL statement Conduit runs is now
    a span inside the operation that ran it, named after the query
    (`GetAuthPolicySettings`, `InsertAuditLog`), so you can see whether a slow
    request is waiting on the database or on something else — previously that time
    was an unexplained gap. Query *values* are never recorded, only the statement
    itself: the parameters are what hold email addresses, tokens and tool
    arguments.

    This adds one span per statement, so traces are more detailed and larger. If
    the volume is more than you want, reduce it with trace sampling in your
    collector rather than expecting a per-signal switch — a trace missing some of
    its spans is harder to read than a trace you kept fewer of.

    Conduit also now sends trace context (the standard W3C `traceparent` header)
    with every outbound request. If you run Conduit alongside your own
    connectors, internal APIs or identity provider and they report to the same
    tracing backend, their spans join Conduit's trace automatically — one trace
    from the AI client's tool call through to your service, with nothing to
    configure. The header carries only randomly generated trace and span ids: no
    hostnames, user or workspace identifiers, or anything about your deployment.
    Application-level tracing "baggage" is deliberately never sent.

  * **Shorter tool names.** AI clients prefix every tool name with a namespace
    of their own, and the strictest reject anything past 64 characters — so a
    Conduit tool could arrive at the model truncated or dropped. Conduit's
    built-in tools are now advertised with no connector prefix
    (`conduit_workspace_admin__update_workspace` → `update_workspace`),
    Pipedream tools use `pd__`, with a shorter slug for a curated set of
    well-known apps (`pipedream__google_sheets-add-single-row` →
    `pd__gsheets-add-single-row`) — an app outside that set keeps its full slug
    rather than being abbreviated into something unreadable. The
    identity-provider tools drop their redundant wording
    (`verify_workspace_identity_provider_domain` →
    `verify_workspace_idp_domain`), and tools from your own MCP connectors are
    unchanged. Access policies, usage and audit records are unaffected — they
    record the full name either way — and a new connector's name is now capped
    at 24 characters, since it is spent from every one of that connector's tool
    names.

  * **Faster tool and resource listings.** When a client asks what tools are
    available, Conduit now queries your connectors at the same time instead of
    one after another, so a listing takes about as long as your slowest
    connector rather than the sum of all of them — the more connectors a
    workspace has, the larger the difference. A slow or unreachable connector no
    longer holds up the healthy ones, and nothing else changes: the same tools
    are listed, in the same order, and a connector that can't be reached still
    either contributes nothing or offers its `authenticate` tool so the user can
    reconnect it.

  * **Outbound connections are pooled and reused.** Conduit kept a separate
    connection pool per user and per connector, so the first tool call each
    person made to a given upstream paid a fresh TCP and TLS handshake even
    though hundreds of other calls to that same host were already in flight. All
    callers now share one pool per upstream, and connections stay warm across
    configuration changes instead of being dropped whenever a policy or connector
    is edited. Tool calls and connector listings lose a round trip or two of
    setup latency, and a busy instance holds far fewer outbound connections —
    which is the number your firewall's per-source connection limits and your
    upstreams' rate limits actually see. Nothing changes about *which* hosts
    Conduit reaches or the SSRF protections applied to them: hosts you exempt
    with `CONDUIT_SAFEHTTP_ALLOWED_PRIVATE_HOSTS` are still pooled separately
    from hosts you don't, so an exemption is never shared with a request that
    wasn't granted it.

  [Compare v0.5.1...v0.6.0](https://github.com/PipedreamHQ/conduit/compare/v0.5.1...v0.6.0)
</Update>

<Update label="v0.5.1" description="2026-07-27">
  *One tracked issue per real failure in error tracking, migrations that apply out of order, connector credentials retired with their user, and a faster web UI after upgrades.*

  ## Upgrade notes

  None.

  ## Changed

  * **Telemetry reports cleaner error signals.** A tool call the client abandons
    mid-flight no longer records a tracked exception in your tracing backend
    (the span still shows as errored), and a failure that crosses several spans
    on its way up records one exception instead of a matched pair — so error
    tracking counts one issue per real failure.
  * Database migrations now apply even when an earlier-numbered migration
    arrives after a later one has already run (e.g. upgrading across a hotfix),
    instead of refusing to start.
  * **The web UI loads faster after an upgrade.** Shared library code is cached
    across releases now, so only the app's own code re-downloads when you pull a
    new version.

  ## Fixed

  * MCP clients that probe `resources/templates/list` now receive an empty list
    instead of a JSON-RPC method-not-found error.
  * Deleting a user now also removes their stored connector credentials; grants
    orphaned by earlier deletions are cleaned up automatically on upgrade.

  [Compare v0.5.0...v0.5.1](https://github.com/PipedreamHQ/conduit/compare/v0.5.0...v0.5.1)
</Update>

<Update label="v0.5.0" description="2026-07-26">
  *Stored credentials are encrypted at rest, auth tables are named after the product, and workspace and account management reach MCP clients. Read the upgrade notes before pulling.*

  ## Upgrade notes

  * **If you run Conduit on PostgreSQL, check who is allowed to create
    workspaces.** A fresh PostgreSQL instance set that policy to "anyone" while a
    fresh instance on the embedded database set it to "instance admins only" — the
    two backends disagreed, and PostgreSQL got the permissive one. New instances
    now start restricted on both. An instance created before this release keeps
    whatever value it has, which for PostgreSQL may be "anyone" through no choice
    of yours, so it is worth confirming: **Settings → Access → workspace
    creation**. We deliberately do not change it for you, because we cannot tell a
    value you chose from one you inherited.

  * **Take a database backup before upgrading, and make sure your encryption key is
    backed up too.** This release encrypts several kinds of stored credential that
    were previously kept as readable text, converting existing rows on the first
    start. That step is automatic and needs nothing from you.

    What it does mean is that **downgrading to an earlier version afterwards will
    not work on the same database.** The values stay encrypted, while an older
    build expects to read them directly — so identity-provider sign-in, email
    sending, and upstream connectors would fail on the older version. If you need
    to go back, restore the backup you took before upgrading.

    Your encryption key also covers more than it did before — see the entries below
    for what, and [`CONDUIT_ENCRYPTION_KEY`](/docs/conduit/configure/reference#security)
    for where it lives.

    The same backup covers a second change with the same shape: several database
    tables were renamed, so an older build would not find them either. Nothing
    reads those tables but Conduit itself, so this is invisible unless you query
    the database directly or have built reporting on top of it — see below.

  ## Added

  * **Workspace settings and members can be managed from an MCP client.**
    Workspace admins get built-in tools to rename a workspace, change its URL slug
    or logo, set a member's role, and remove a member — the same actions the
    workspace settings pages offer, under the same rules: you cannot grant a role
    above your own, change your own role, or leave a workspace without an
    administrator.

  * **You can see who you are signed in as, and which workspaces you belong to,
    from an MCP client.** Any user gets built-in tools to read their own account
    and the workspace they are working in, to list the workspaces they are a member
    of, and to create one — the last subject to the same rules as the web app, so
    it is refused if your instance runs in single-workspace mode or reserves
    workspace creation for instance administrators. Only your own memberships are
    listed: being an instance administrator does not put every workspace in that
    list.

  ## Changed

  * **Connectors can no longer be saved with the unimplemented `token-exchange`
    auth type.** It was accepted at save time and could never work: exchanging a
    token on a member's behalf needs the token their identity provider issued, and
    Conduit does not retain one, so such a connector looked configured and served
    no tools. It was never offered in the connector UI or by any catalog entry, so
    reaching it required calling the API directly. That call now returns an
    explanatory error instead of appearing to succeed.

  * **Database tables are named after what the product calls them.** The tables
    behind users, workspaces and provisioning carried a `ba_` prefix, left over
    from the library this instance's auth layer was first ported from. It described
    where the code came from rather than what the data is, so anyone reading the
    database had to know a piece of Conduit's history to find the users table.
    `ba_user` is now `users`, `ba_organization` is `workspaces`, `ba_member` is
    `workspace_members`, `ba_assignment_rule` is `provisioning_rules`, and so on
    through sixteen tables.

    This is internal plumbing: no API, RPC, or UI field changes, and the upgrade
    renames existing tables in place without touching a row of data. It matters
    only if you query the database directly — a reporting tool or a support script
    pointed at `ba_*` needs the new names.

  ## Fixed

  * **Revoking a user's sessions now tells you if it didn't work — and always
    takes their refresh tokens too.** Revoking sessions from the admin pages
    reported success whatever happened, so a failed revoke looked identical to a
    successful one and was recorded in the audit log as having happened. For a
    control you reach for when you believe an account is compromised, that is the
    worst possible failure: you move on believing access is cut. The two halves of
    the revoke — the sessions themselves and the OAuth refresh tokens that can mint
    new ones — now also succeed or fail together, so a partial failure can no
    longer leave a token behind that quietly restores access.

  * **Deleting an identity provider now reports failure, and never leaves its
    claimed domains behind.** A failed delete answered with success, so an
    administrator could believe a sign-in route was closed while it still worked.
    The provider and the domains it claims for sign-in routing are now removed
    together; previously a failure between the two could leave a domain pointing at
    a provider that no longer existed.

  * **Saving instance sign-in settings now reports failure instead of showing you
    your old values as if they saved.** The page answered from a fresh read, so a
    write that didn't land came back looking successful with the previous settings
    displayed — and the audit log recorded a change that never happened. These
    settings are also now saved together: a partial failure could previously leave
    the instance in a combination of sign-in options nobody chose, such as
    requiring SSO while re-permitting passwords.

  * **A GraphQL or OpenAPI connector whose credential stopped working now offers
    you a reconnect link.** When an upstream rejected a saved credential — an
    account disconnected on their side, a token revoked or expired — an MCP
    connector answered with the prompt to reconnect, but a GraphQL or OpenAPI one
    answered with the raw rejection text, which reads as a broken tool rather than
    as something you can fix. All three now offer the same prompt. As before, this
    applies only where you are the one who connected the credential: an upstream
    rejecting a workspace-wide credential set by an admin still reports the
    upstream's own explanation, since reconnecting is not yours to do.

  * **An upstream credential is no longer used so close to its expiry that it can
    lapse mid-request.** Conduit refreshes a cached upstream token shortly before
    it expires. For connectors using SSO token exchange, and for connected
    accounts, that safety window was shorter than the time a single upstream call
    is allowed to take — so a token could pass the check, then expire while the
    request carrying it was still in flight, and the call came back rejected even
    though the credential had been valid when it was chosen. The window is now
    wider than the longest a call can run, for every kind of connector.

    Also fixed in the same place: a token exchange whose result was valid for
    under thirty seconds was cached as though it had already expired, so a fresh
    exchange ran on every request instead of once.

  * **A Pipedream access token is no longer re-fetched on every single request.**
    Conduit caches the token it mints for Pipedream until shortly before it
    expires. When Pipedream's reply omitted how long the token was good for —
    which the OAuth specification permits — Conduit read that as "already
    expired" and minted a fresh one for every upstream call, adding a round trip
    to each and burning through token requests. A reply with no stated lifetime is
    now treated as good for an hour.

  * **A stalled token endpoint no longer holds up the requests behind it.** When
    Conduit fetched a credential from an identity provider, a connector's token
    endpoint, or Pipedream, the fetch ignored the fact that the request that
    needed it had been abandoned — so a slow endpoint was waited out in full even
    though nobody was left to receive the answer, and in Pipedream's case every
    other caller queued behind it. These fetches now stop as soon as the work is
    no longer wanted.

  * **A failing token endpoint now says what went wrong.** If a token endpoint
    answered with something other than the expected JSON — typically a proxy's
    HTML error page during an outage — the error recorded was a complaint about
    invalid JSON syntax, with no mention of the status code that would have
    identified it as an outage. The status is now included. Relatedly, a reply too
    large to accept is reported as such rather than as malformed.

  * **Renaming a workspace to a slug that is already taken no longer changes its
    name anyway.** The name, URL slug and logo were saved one after another. If you
    edited more than one and the slug collided with another workspace, the earlier
    fields had already been written by the time the collision was reported — so the
    error said the slug was taken while the workspace had quietly been renamed. All
    three are now saved together, and a rejected slug leaves the workspace exactly
    as it was.

  * **Switching workspace now tells you if it failed.** The write that records
    which workspace you are working in was made with its error discarded: a failed
    switch was logged on the server and answered as a success, so the app showed
    you the workspace you had picked while the next page load put you back in the
    old one. It now surfaces the failure.

  * **A new workspace and its owner are created together.** The workspace row and
    the membership that makes you its owner were written one after the other. If
    the second failed, the workspace existed with no members at all — invisible in
    your workspace list, and removable only by an instance administrator. Both now
    land in one step, so a failure leaves nothing behind.

  * **A workspace can no longer be created with a blank name.** A name of only
    spaces was accepted, producing a workspace with no readable name and a URL slug
    of dashes.

  * **An SSO sign-in's state token is now spent exactly once.** The identity
    provider's callback carries a one-time state value that Conduit checks and then
    discards. It was checked and discarded in two steps, so two callbacks arriving
    together with the same value could both pass the check before either cleared
    it. The exchange that follows would still fail for the second one, since the
    provider only honours its authorization code once, but the guard that is
    supposed to be single-use was not. It is now consumed in a single step.

    Relatedly, if Conduit cannot store that state at the start of a sign-in, the
    sign-in now fails at that point rather than sending you to your provider for a
    round trip that could only come back rejected.

  * **Editing a user or a workspace from the admin pages now reports a failure
    instead of claiming success.** Both saves wrote their fields with the error
    discarded, so a write that did not land still answered with a success. Renaming
    a workspace additionally hid every failure except a duplicate slug, which is
    the one it did report. Both now surface what went wrong.

  * **Saving a workspace's join settings now tells you if it failed.** A failed
    write was logged on the server and otherwise ignored: the page answered with
    the settings as they were re-read afterwards, so an admin saw a success and
    their old values, with no indication the change had not been stored. It was
    also recorded in the audit log as a change that happened. Discovering
    workspaces to join had the same shape, reporting a failed lookup as "no
    workspaces available". Both now surface the error.

  * **A database hiccup no longer crashes the workspace, members and access pages,
    and no list can quietly come back short.** Several screens loaded their rows in
    a way that mishandled a failed query: instead of reporting the failure, the
    request crashed outright. Others could return a partially-read list — a members
    roster missing members, a workspace's identity providers missing providers —
    with nothing to indicate anything was missing, which is worse than an error,
    because an admin reasonably reads the page as the truth. Reads now either
    return the complete result or report a failure you can see and retry.

  * **Identity-provider tokens are now encrypted at rest.** When a member signs in
    through SSO, Conduit keeps the tokens the identity provider issued so it can
    exchange them for upstream access on that member's behalf. Those were stored as
    readable text: anyone with a copy of the database or a backup could read them
    and act as your members against the identity provider. They are now encrypted
    with the same instance key as every other stored credential, and existing rows
    are encrypted in place on the first start after upgrading.

  * **Connected-account credentials and workspace join links are now encrypted at
    rest.** The access and refresh tokens Conduit holds on your behalf for an
    upstream connector — the ones minted when a member connects their account, or
    when an admin connects one credential for the whole workspace — were stored in
    the database as readable text, as were per-member API tokens and the secret in
    a workspace's join link. Anyone holding a copy of the database file or a backup
    could read them and call those upstream services as your members. They are now
    encrypted with the same instance key that already protected identity-provider
    and email credentials, and existing rows are encrypted in place the first time
    the upgraded instance starts, so there is nothing to run by hand.

    This makes the encryption key load-bearing for connected accounts: if
    `<data-dir>/conduit.key` (or `CONDUIT_ENCRYPTION_KEY`) is lost, members have to
    reconnect their accounts and admins have to regenerate join links. Back the key
    up.

  * **Changing a member's workspace role now reaches their connected AI clients.**
    A role change updated the workspace immediately, but announced nothing: a user
    already connected over MCP kept being offered the admin tools of the role they
    no longer held, and someone newly promoted saw none of their new tools, until
    they reconnected. Calling a tool they had lost was always refused — permission
    is re-checked on every call, so this was never a way to keep access — but the
    list an assistant chooses from could be out of date for as long as the
    connection stayed open. Role changes made through provisioning rules, and the
    automatic owner promotion that follows demoting the last one, were affected the
    same way and are fixed too.

    Connections now resolve who you are each time they recompute your tools rather
    than remembering it from when you connected, so a change made anywhere — by
    another instance in a multi-replica deployment, or directly in the database —
    is picked up as well.

  [Compare v0.4.1...v0.5.0](https://github.com/PipedreamHQ/conduit/compare/v0.4.1...v0.5.0)
</Update>

<Update label="v0.4.1" description="2026-07-25">
  *Failures now surface as issues in APM error tracking, with service-entry spans for every request and W3C trace propagation from MCP clients.*

  ## Upgrade notes

  None.

  ## Changed

  * **Errors in traces now surface in APM error tracking.** Every HTTP request
    (RPCs, MCP calls, OAuth, SCIM) gets a service-entry span named after its
    route, and failures the operator should act on — upstream transport errors,
    credential refresh/exchange failures, handler panics, unhandled 500s — carry
    a full exception (stable `exception.type`, message, stack trace), which is
    what backends like Datadog require to open an issue. Expected failures
    (a tool result reporting an error, a credential the user hasn't connected
    yet) stay visible in traces but deliberately never become issues, and
    exception types/messages are kept stable so one failure mode groups into one
    issue. Conduit also honors an inbound W3C `traceparent`, so a client that
    traces its own agent loop sees Conduit's spans in the same trace; health
    probes, static assets, and idle MCP event streams are not traced.

  [Compare v0.4.0...v0.4.1](https://github.com/PipedreamHQ/conduit/compare/v0.4.0...v0.4.1)
</Update>

<Update label="v0.4.0" description="2026-07-25">
  *Refused requests and upstream-credential writes in the audit log, server-wide request body caps, SSRF hardening on every outbound call, forwarded client IPs trusted only from proxies you declare, and connector repointing that revokes stale credentials.*

  ## Upgrade notes

  * **If a reverse proxy fronts your instance, set `CONDUIT_TRUSTED_PROXIES` to
    its address range.** Conduit no longer trusts forwarded client-IP headers from
    private-range addresses by default; it now trusts loopback only. This affects
    any deployment whose proxy reaches Conduit over the network rather than over
    loopback — a Kubernetes ingress controller (`CONDUIT_TRUSTED_PROXIES=10.42.0.0/16`,
    your pod CIDR), or `docker run -p`, where traffic arrives from the bridge
    gateway (`172.17.0.0/16`). Sidecar proxies talking to Conduit over loopback
    need no change.

    Left unset, such an instance still works, but every audit entry and per-IP
    rate limit resolves to the proxy's own address, and Conduit logs a warning
    once per process naming the peer it saw — that address is the one to trust.
    See [client IP attribution](/docs/conduit/configure/reference#client-ip-attribution).

  * **If your instance reaches the Pipedream API, Resend, or Amazon SES through an
    egress proxy on a private address, list that proxy's hostname in
    `CONDUIT_SAFEHTTP_ALLOWED_PRIVATE_HOSTS`.** Those three calls now go through
    the same SSRF-hardened client as every other outbound request, which refuses to
    connect to private addresses. Instances that reach the internet directly need
    no change. A blocked call fails with `blocked connection to special-use
    address` naming the host.
    See [Network Access](/docs/conduit/deploy/network).

  * Database migrations apply automatically on boot; nothing else to do.

  ## Changed

  * **Repointing a connector revokes the credentials members stored for it.**
    Editing a connector's URL, or its OAuth client id, authorization endpoint, or
    token endpoint, now drops every credential held for the old destination — each
    member's connected account and the shared workspace credential — and members
    reconnect to re-consent. A credential is only valid for the upstream it is
    sent to and the authorization server that issued it, so carrying it to a new
    destination would have handed those tokens (and, on the next refresh, the
    connector's client secret) to whoever now sits behind the new address. Every
    such edit is recorded in the audit log as `config.connector.repointed`, with
    the field that changed, its previous value, and how many credentials were
    dropped.

  ## Security

  * **Every request body and every upstream response is bounded.** Conduit capped
    bodies at each endpoint that read one, which meant the cap was something a new
    endpoint had to remember. Request bodies are now capped for the whole server
    before any route sees them — 1 MiB on pre-auth surfaces, 10 MiB where a large
    payload is legitimate (the API, `/mcp`, SCIM) — so a route cannot be added
    without one. In the other direction, replies from services Conduit calls out to
    are now capped as well: an identity provider's token or discovery response, an
    upstream's OAuth metadata, or a vendor API's JSON was read without limit, so a
    compromised or hostile peer answering with an endless body could exhaust the
    gateway's memory. Oversized replies are refused rather than truncated, so a
    short read can never be mistaken for a complete one.

  * **All outbound requests go through the SSRF-hardened client.** Identity
    providers, upstream MCP servers, connectors, and OAuth metadata documents
    already did. The Pipedream API and the Resend and Amazon SES email providers
    now do too, so the protection is what a newly added outbound call inherits
    rather than something each one opts into. The new
    [Network Access](/docs/conduit/deploy/network) page is now the single list of every
    host Conduit connects to, what each is for, and when it is contacted.

  * **Forwarded client-IP headers are only trusted from proxies you declare.**
    Conduit attributes audit entries and keys its per-IP rate limits (sign-in,
    sign-up, OAuth, failed MCP auth) on the client IP. It previously accepted
    `X-Forwarded-For` from any private-range peer, so on a shared network — a
    Kubernetes cluster without NetworkPolicies, a Docker bridge, or the LAN of an
    on-prem install — a caller could choose the value. That let them give every
    request a fresh rate-limit bucket, which defeats the per-IP throttles for
    password spraying across many accounts, and write an arbitrary string into the
    audit log, which breaks attribution and could implicate a real address. The
    default trusted set is now loopback only — every other proxy is listed
    explicitly as an IP or CIDR in `CONDUIT_TRUSTED_PROXIES` — and a forwarded
    value must parse as an IP address to be used at all.

  * **Refused requests are recorded in the audit log.** Permission checks used to
    be silent: an authenticated member could call every instance-admin and
    workspace-admin RPC, and name every builtin MCP tool, collecting hundreds of
    refusals without producing a single audit entry — so the reconnaissance that
    precedes an attack left no trace, and only the operations that succeeded were
    ever visible to an admin reviewing the log afterward. Refusals now appear as
    `authz.role.denied` (the caller's role doesn't reach that method or builtin
    tool) and `authz.policy.denied` (their access policy doesn't grant that
    connector tool), each naming the actor, the target, and the workspace, and
    each recording whether the call arrived over HTTP or MCP. The two are separate
    events because they call for different responses — the first says someone is
    reaching above their role, the second may just mean a policy needs widening —
    and filtering the `authz` group shows both. They are deliberately not sampled:
    a burst of denials from one account is the signal, and dropping its tail would
    drop the evidence. Denied tool calls also count in the Usage dashboards, where
    they previously went uncounted.

  * **Upstream connector credentials are recorded when set or cleared.** Storing a
    static token, completing an OAuth connect flow, and disconnecting all now
    write `connect.upstream_credential.set` / `.cleared`, naming the connector,
    whether the credential is the caller's own or the shared workspace one, and —
    for a workspace credential, which every member uses — the admin who connected
    it. The credential plane was the one place a write left no record, which made
    it the cheapest place to establish persistence. This adds a database migration
    that runs automatically on boot; no operator action is needed. Connect flows
    already in progress across the upgrade are unaffected.

  * **A removed member's open MCP stream stops receiving a workspace's change
    signals.** On the bare `/mcp` endpoint, where the client names no workspace,
    a stream held open across a membership change kept delivering that workspace's
    `list_changed` notifications — which connectors and tools changed there, and
    when — to someone who had since been removed from it. Such a stream now closes
    as soon as its token resolves to a different workspace, and the client
    reconnects against its current access.

  * **Failed MCP authentication is reported even while the throttle is active.**
    Repeated bad-token requests to `/mcp` are rate-limited per IP; the throttled
    attempts were previously dropped before their telemetry was recorded, so a
    brute-force burst registered as a trickle. Every attempt is now counted and
    reported.

  * **Every pre-auth request body is size-capped.** The OAuth consent callback
    decoded its JSON body, and the token endpoint parsed its form, before either
    checked the caller's session — so an unauthenticated client could stream
    oversized bodies into memory in parallel, degrading the instance for all
    workspaces. Both now reject anything past the 1 MiB pre-auth cap that the rest
    of the unauthenticated surface already enforced. The
    [install guide](/docs/conduit/deploy/install#what-the-fronting-proxy-is-responsible-for)
    now states which abuse Conduit handles itself and which it expects a fronting
    proxy to absorb.

  * **A failed audit-log write is no longer silent.** If the audit insert itself
    fails, Conduit logs an error and emits `conduit.audit.write_failed` to your
    OTLP backend, rather than discarding the failure — a broken audit pipeline
    used to be indistinguishable from a quiet system.

  [Compare v0.3.0...v0.4.0](https://github.com/PipedreamHQ/conduit/compare/v0.3.0...v0.4.0)
</Update>

<Update label="v0.3.0" description="2026-07-24">
  *SCIM 2.0 provisioning, a guarantee that every signed-into workspace keeps an owner and an admin, canonical emails and unique memberships, and a linkable Connectors page.*

  ## Upgrade notes

  Migrations apply automatically on boot. Three of them rewrite existing rows,
  and one wants a follow-up from an admin:

  * **Group names that collide only by case are made unique.** Collisions already
    in the database are kept rather than merged: the later group gains a
    `[<group id>]` suffix, visible in **Settings → Policies** for an admin to
    rename to whatever it should be.
  * **Stored email addresses are normalized** (lowercased, trimmed), and
    **duplicate workspace and group memberships** left by earlier races are
    collapsed, keeping the highest-privilege one. Nothing to do.
  * **Pending MCP authorization codes are cleared.** A client that was mid-flow
    at the moment of the upgrade re-authorizes on its own.

  ## Added

  * **SCIM 2.0 provisioning.** An identity provider (Okta, Microsoft Entra, or
    any SCIM 2.0 IdP) can push users, groups, and group memberships into a
    workspace, so joiners, movers, and leavers in your directory reach Conduit
    without anyone signing in first. Workspace admins generate bearer tokens in
    **Settings → SCIM**, each bound to one of the workspace's SSO providers so
    provisioned users and sign-ins resolve to the same person. Deprovisioning
    removes the user from the workspace, revokes their sessions, and ends the
    group memberships and policy grants they held there; reactivating them
    restores the memberships the IdP still asserts, since an IdP pushes group
    changes as deltas and never re-sends one its own directory didn't change.
    Grants that came from anywhere else are not resurrected — a membership an
    admin added by hand stays that admin's to re-make. Pushed groups become
    Conduit groups usable in access policies, marked **SCIM** in
    **Settings → Policies** because their name and their IdP-pushed members are
    reconciled to the directory on every sync; memberships added by an admin or
    an assignment rule are never touched by a sync. See
    [SCIM Provisioning](/docs/conduit/configure/scim) for connecting an IdP.

    A token carries the authority of the workspace admin who generated it and no
    more. It cannot deprovision the workspace's owner, change the name or email
    of someone a different identity provider established, or rename or
    deprovision an instance administrator — each of those reaches past the
    workspace the token belongs to. Two provisioned people in one workspace also
    cannot share a `userName`: an address can legitimately belong to several
    federated identities, while SCIM requires `userName` to identify exactly one
    user, so the second push is refused rather than leaving an IdP's
    match-before-provision lookup with two answers.

    Every provisioning change is recorded in the workspace audit log, attributed
    to the connection's SSO provider — and so is every push Conduit refuses, with
    the reason, since a refusal repeats on every sync until a human resolves it
    and belongs where the workspace's admins will see it rather than only in the
    IdP's own logs. Conduit refuses rather than guesses: a membership push it
    cannot interpret exactly, or a `remove` of an attribute it cannot unset, is
    answered with an error instead of a `200` the IdP would read as applied,
    because the nearest guess at "which members?" is an empty group.

  ## Changed

  * **A workspace people can sign into always keeps at least one administrator —
    and an owner.** Removing or demoting a member is refused — on every path: the
    members UI, the instance admin API, SCIM deprovisioning, and deleting the
    user's account outright — when it would leave the workspace's remaining
    members with no owner or admin. Removing the *only* member is allowed (nobody
    is left to be locked out), which is what keeps a user's own workspace from
    making their account undeletable; the workspace stays in place for an
    operator to keep or delete. An owner can be removed or demoted while another
    admin remains, and when that leaves the workspace owner-less the
    longest-standing admin is promoted to owner automatically, so owner-only
    actions (like deleting the workspace) stay reachable. Removals honor the same
    rank ceiling as role changes — you cannot remove a member who outranks you,
    so an admin cannot evict the owner and inherit ownership — and an instance
    administrator can only be removed or renamed by another instance admin,
    never by anything scoped to a single workspace. Deleting a member revokes the
    group memberships and policy grants they held in one step with the removal,
    so a partial failure cannot leave grants behind.
  * **Memberships are enforced unique in the database.** A person's workspace
    membership and a group's member entries are backed by unique indexes, so
    concurrent joins (invite acceptance, sign-in auto-join, SCIM sync,
    assignment rules) can no longer create duplicate rows on Postgres. Group
    membership rows are also foreign-keyed to their group, so deleting a group
    removes them in the database itself and a membership can never outlive its
    group. Group member counts include only people who still have an account, so
    a deleted user can no longer show up in a group an admin cannot remove them
    from.
  * **Group names are unique per workspace without regard to case.** They were
    already *compared* that way, so "Engineering" and "engineering" could both
    exist while a lookup for either matched both — which left an identity
    provider's group sync picking one arbitrarily.
  * **Emails are stored in one canonical form.** Addresses are normalized
    (lowercased, trimmed) on the way in, so sign-in, SSO, and provisioning always
    resolve the same person regardless of the casing an identity provider or a
    sign-up form sent.
  * **OAuth clients receive sessions for exactly the user who authorized them.**
    Authorization codes carry the user's id rather than their email address. An
    address can legitimately belong to more than one identity, so resolving by
    email could mint a session for the wrong account.
  * **Unchecking a connector's last tool disables the connector.** On the
    Connectors page, tool checkboxes and the enable switch now follow each
    other in both directions: checking a tool enables the connector (as
    before), and unchecking the last one turns it off instead of leaving it
    enabled with nothing selected.
  * **The Connectors page is linkable.** Selecting a connector navigates to
    `/connectors/<id>` (e.g. `/connectors/app:linear`), and the two search
    boxes persist as `?q=` (connector search) and `?tools=` (tool filter) —
    so a specific connector's tool view can be bookmarked or shared, and
    reloading or navigating back restores what you were looking at. Opening
    `/connectors` bare lands on the first connector's URL, and a link to a
    connector that no longer exists falls back there too.

  ## Fixed

  * **Searching connectors no longer drops keyboard focus.** In the policy
    grant editor and on the Connectors page, the search box could lose focus
    mid-word whenever a search crossed between "no matches" and "matches";
    typing now stays uninterrupted, and the no-matches message appears in
    place of the connector list.
  * **The Connectors page no longer flashes a built-in connector while
    loading.** The detail pane (on the Connectors page and in the policy
    grant editor) now shows a loading placeholder until the real connector
    list arrives, instead of briefly showing a built-in connector and then
    switching.
  * The home dashboard's Top connectors and Top tools lists now show the Conduit
    logo for built-in Conduit tools instead of a generic plug icon.

  [Compare v0.2.7...v0.3.0](https://github.com/PipedreamHQ/conduit/compare/v0.2.7...v0.3.0)
</Update>

<Update label="v0.2.7" description="2026-07-23">
  *Search-engine exclusion, instant hover-preloaded navigation, a real error page, and resilience fixes for crashes and upgrades.*

  ## Upgrade notes

  None.

  ## Added

  * **Search engines are told to stay out.** Every response carries an
    `X-Robots-Tag: noindex` header and the instance serves a disallow-all
    `robots.txt`, so a Conduit dashboard reachable from the internet is
    neither crawled nor indexed.

  ## Changed

  * **Navigation feels faster.** Hovering a link preloads the destination
    page's code and data, so most navigations render instantly.
  * **Unexpected page errors get a real error page.** If a page fails to load
    or render, the app shows a styled "Something went wrong" page with the
    error and a retry button, instead of an unstyled developer message.

  ## Fixed

  * **Deploying no longer breaks already-open tabs.** A tab opened before an
    upgrade could fail on its next page navigation (the old page code it asks
    for is gone); the app now reloads itself once to pick up the new version.
  * **A crashing request is a logged 500, not a vanished connection.** If a
    request handler hits an unexpected internal error, the client now gets a
    clean HTTP 500 and the crash is recorded in the server log and as a
    `conduit.server.panic` telemetry event, instead of the connection simply
    dropping with nothing written anywhere.

  [Compare v0.2.6...v0.2.7](https://github.com/PipedreamHQ/conduit/compare/v0.2.6...v0.2.7)
</Update>

<Update label="v0.2.6" description="2026-07-23">
  *Per-page browser tab titles, instant app connect links, and big database-efficiency gains for session validation and connection pooling.*

  ## Upgrade notes

  None.

  ## Changed

  * **Browser tabs show the page name.** Every page in the dashboard sets its
    own tab title (Members, Usage, Connect Slack, …) instead of every tab
    reading "Conduit", so multiple open tabs are distinguishable.
  * **App connect links go straight into the connect flow.** Opening
    `/connectors/app:<slug>/connect` (the link Conduit hands to MCP clients)
    launches the account-connect flow immediately instead of showing a page
    with a Connect button — and shows a "connected" confirmation right away
    when the account already exists. The page also warms the connection to
    the connect service while loading, so the flow appears sooner.
  * **Session validation costs one database query instead of three.** Every
    API request, MCP request, and SSE keepalive validates its session with a
    single joined lookup (session + user + membership) rather than three
    sequential round-trips. Not a cache — revocation still takes effect
    immediately. PostgreSQL tool-call throughput +11%, dashboard API +30% at
    the standard pod shape; idle SSE streams cost a third of the database
    reads. See the [load tests](/docs/conduit/load-tests).

  ## Fixed

  * **Database connection pools no longer churn under load.** The pools kept
    only Go's default 2 idle connections, so heavy traffic closed and
    re-dialed connections constantly — on PostgreSQL this could exhaust the
    pod's ephemeral ports and fail requests (measured at \~1,000 concurrent
    tool calls). Idle capacity now matches pool capacity, with a 5-minute
    idle shed so a quiet instance releases its connections back to a shared
    database server. PostgreSQL tool-call throughput at the standard pod
    shape rose from 850/s (with errors) to 3,600/s (clean) — see the
    [load tests](/docs/conduit/load-tests).

  [Compare v0.2.5...v0.2.6](https://github.com/PipedreamHQ/conduit/compare/v0.2.5...v0.2.6)
</Update>

<Update label="v0.2.5" description="2026-07-22">
  *Full-screen split view with mobile fixes, faster SSE connects, container-aware memory limits, load-test docs, and an optional debug endpoint.*

  ## Upgrade notes

  None.

  ## Added

  * **Published capacity test results.** A new [Load tests](/docs/conduit/load-tests)
    docs section records dated capacity passes — sustained per-pod ceilings
    per deployment tier, with full environment provenance so numbers stay
    comparable across versions. The measuring harness ships in the repo
    (`loadtest/`).
  * **Debug endpoint for profiling and load analysis.** Setting
    `CONDUIT_DEBUG_ADDR` (e.g. `localhost:6060`) starts a second listener
    serving Go pprof profiles and runtime/DB-pool statistics. Off by default;
    bind it to localhost or a private interface only. See the
    [configuration reference](/docs/conduit/configure/reference).

  ## Changed

  * **The runtime respects the container memory limit.** Conduit derives the
    Go garbage collector's soft memory limit (`GOMEMLIMIT`) from the
    container's cgroup ceiling automatically (90% of it; setting `GOMEMLIMIT`
    yourself still wins). Memory-dense workloads — many held SSE streams —
    now degrade gracefully under GC pressure instead of being OOM-killed,
    and fit \~50% more connected users per pod. See the
    [load tests](/docs/conduit/load-tests).

  ## Fixed

  * **SSE connections are no longer serialized at connect.** Registering an
    MCP SSE stream previously performed upstream work while holding a global
    lock, so a reconnect burst (for example after a deploy) could delay — or
    under a slow upstream, freeze — all new SSE connections on the instance.
    Connecting is now instant regardless of upstream latency or burst size;
    clients may receive one extra `list_changed` notification after
    connecting (they re-list, which is always safe). See the
    [load tests](/docs/conduit/load-tests) for measurements.
  * Split-view polish on the Connectors page and the policy grant editor: the
    Connectors page no longer scrolls as a whole — the connector list and tool
    pane fill the screen and scroll independently — headers and the checkbox
    column align, and connector descriptions truncate to one line with a
    "more" toggle.
  * Expanding a tool row no longer shifts its description text, and
    documentation links inside a row open without collapsing the row.
  * On phones, tool rows stack the name above the description instead of
    squeezing the description into a sliver, and the tool filter box spans the
    full width.
  * On cramped screens — phones in either orientation, or any short window —
    the workspace-admin "Manage connectors" banner on the Connectors page
    collapses to a one-line link, so the connector list keeps the screen
    space (the page header is pinned and no longer scrolls away).
  * The Groups & Policies page fits phone screens: its dialogs no longer
    overflow the viewport, and the connector icon strip shows fewer icons (with
    a higher +N count) instead of running off the edge.
  * The policy grant dialog makes better use of cramped screens: on narrow
    phones and short windows it hides its helper sentences and goes
    full-height, so the connector list keeps several rows visible instead of
    two. When "Allow all connectors" is on, the dialog now shrinks to fit the
    summary instead of showing a mostly-empty full-height box.
  * On a fresh load of the Connectors page (and the policy grant editor), the
    split view no longer defaults its selection to a built-in connector while the
    app catalog is still loading — the selection lands on the first real
    connector once the list fills in.

  [Compare v0.2.4...v0.2.5](https://github.com/PipedreamHQ/conduit/compare/v0.2.4...v0.2.5)
</Update>

<Update label="v0.2.4" description="2026-07-22">
  *Split-view browsing and configuration for connectors and tools, plus a wider audit log layout.*

  ## Upgrade notes

  None.

  ## Changed

  * The audit log pages use a wider layout on large screens, giving the event
    table and the detail panel more room.
  * Browsing and configuring connectors and tools — on the Connectors page and in
    the policy grant editor — now uses a split view: the connector list sits on
    the left and the selected connector's tools on the right. Each tool shows as
    a compact one-line row that expands for its full description, and connectors
    with many tools gain a filter box to narrow the list. The connector list
    loads more automatically as you scroll — no more Previous/Next paging through
    the Pipedream app catalog. Tool checkboxes work even while a connector is
    toggled off — checking a tool enables the connector with just that tool
    selected. In the policy grant editor, workspace admins now see a connector's
    full tool catalog, including connectors that haven't been granted to anyone
    yet.

  [Compare v0.2.3...v0.2.4](https://github.com/PipedreamHQ/conduit/compare/v0.2.3...v0.2.4)
</Update>

<Update label="v0.2.3" description="2026-07-21">
  *Fixes the Connectors page showing no apps in workspaces whose allow policies name specific apps.*

  ## Upgrade notes

  None.

  ## Fixed

  * The Connectors page again lists apps in workspaces whose allow policies name
    specific apps (rather than granting all apps): the Pipedream lookup that
    resolves those app slugs sent a query the API rejected, and the page rendered
    no apps at all — leaving members nothing to enable. Failures of that lookup
    now surface as an error instead of an empty app list.

  [Compare v0.2.2...v0.2.3](https://github.com/PipedreamHQ/conduit/compare/v0.2.2...v0.2.3)
</Update>

<Update label="v0.2.2" description="2026-07-21">
  *In-client connector auth — connect prompts, URL elicitation, a focused connect page — plus the client-credentials grant and a unified reconnect UX.*

  ## Upgrade notes

  * All schema migrations in this release apply automatically on boot; no
    manual steps.
  * **Active-active deployments**: configure session-sticky load balancing
    (by bearer token or `Mcp-Session-Id`) so in-band MCP prompts work — see
    the entry under **Changed** and
    [Deployment Tiers](/docs/conduit/deploy/availability).

  ## Added

  * OpenAPI and GraphQL connectors can now authenticate upstream with the
    **OAuth client credentials grant**. Pick "Client credentials" as the grant
    type in the connector's authentication settings and supply the client ID,
    client secret, and token endpoint (plus optional scopes and audience) — no
    authorization URL is needed. Conduit exchanges the credentials for a
    workspace-wide token itself and refreshes it before expiry, so members never
    connect their own accounts.
  * Tool lists now show the HTTP method and path under each OpenAPI-derived
    tool (e.g. `GET /v1/apps`), on the connectors page and in policy grant
    editing — specs can declare several operations with identical summaries, and
    the operation is what tells them apart.
  * The connector wizard now reads the security schemes an OpenAPI spec declares
    and preselects the matching authentication method — prefilling the
    authorization and token endpoints for OAuth authorization-code flows, the
    token endpoint for client-credentials flows, and choosing the API token
    method for API-key and bearer schemes. Detection is a prefill only:
    everything stays editable, and specs without a recognizable scheme leave the
    wizard unchanged.
  * Identity providers can now carry an **access denied help message**, shown to
    users when the provider refuses a sign-in with `access_denied` — for
    example, when a user isn't assigned to the app in Okta or Microsoft Entra.
    Admins configure it per provider (instance sign-in providers and workspace
    SSO providers both), as plain text where pasted URLs and
    `[label](https://url)` spans render as links — useful for pointing users at
    a docs portal or an access-request form. The message also appears on the
    login page as an info popover next to that provider's sign-in button. The
    error page now names the provider in its fixed copy ("Sign in failed for
    Okta. Reach out to your admin for assistance.") when the sign-in flow is
    known. The backing schema migration runs automatically on upgrade.
  * Connectors that need a personal credential are now discoverable from MCP
    clients: instead of silently hiding an unconnected connector's tools, the
    gateway lists a `<connector>__authenticate` tool that returns a link to
    connect your account, and calls to a not-yet-connected connector's tools
    return the same link instead of a raw authentication error. Once you
    connect, tool lists refresh automatically.
  * A focused **connect page** (`/connectors/<id>/connect`) is the landing
    target of those links: one connector, one action — authorize (OAuth), paste
    a key (token), or connect an app account (Pipedream) — then return to your
    assistant.
  * App tool calls (via Pipedream) for an app without a connected account get
    the same connect prompt as every other connector, instead of passing
    Pipedream's raw connect-link text through.
  * **In-client connect prompts via URL elicitation.** When your MCP client
    supports URL-mode elicitation (MCP 2025-11-25), connecting an account is
    offered natively in the client: the `<connector>__authenticate` tool asks
    for consent to open Conduit's connect page, and calling a tool whose
    credential is missing or no longer works returns the standard
    `URLElicitationRequiredError` so capable clients can open the page and
    retry automatically once you've connected. Credentials are always entered
    on Conduit's session-gated connect page — never in the MCP client — per the
    MCP spec's requirement that sensitive values not pass through clients.
    Clients without URL elicitation get the connect link as text, as before.
  * The MCP protocol version is now negotiated per session: Conduit echoes the
    client's requested version when supported (2025-03-26 through 2025-11-25)
    instead of always answering 2025-03-26.

  ## Changed

  * The built-in `conduit__get_connectors` tool is now
    `conduit__get_my_connectors` — the name now says whose connectors it lists
    (the caller's own, filtered by their access policies). MCP clients calling
    it by the old name must switch to the new one.
  * The built-in `get_workspace_connectors` tool moved from the everyone-visible
    `conduit` connector to `conduit_workspace_admin`, and the RPC behind it is
    now workspace-admin only. It returns the workspace's full connector catalog
    for policy management — every connector including disabled ones, with an
    `enabled` field carrying the real state — rather than the caller's view of
    what is currently live. Members keep `get_my_connectors` for that.
  * The **Connectors** and **Accounts** pages now manage every connection the
    same way, first-time or not: a connected connector shows its credential type
    with an inline **Reconnect** / **Disconnect** menu instead of a bare
    "Connected" label. Applies to OAuth, per-user token, and Pipedream app
    connectors alike.
  * **Active-active deployments now need session-sticky load balancing** for
    in-band MCP prompts (elicitation/sampling): the gateway holds such a tool
    call open on one replica and waits for the client's answer on a separate
    request, which must return to the same replica. Route each MCP connection to
    a consistent replica (by bearer token or `Mcp-Session-Id`) — everything else
    remains replica-independent. Conduit warns at startup in this mode. See
    [Deployment Tiers](/docs/conduit/deploy/availability).

  ## Fixed

  * Connecting, reconnecting, or disconnecting a connector now refreshes its tool
    list right away — in the web UI (the connector's tools re-fetch, so a
    reconnect clears a stale error instead of leaving it cached) and for connected
    MCP clients (connect/disconnect now emits `notifications/tools/list_changed`).
  * A connector whose saved credential the upstream rejects (expired or revoked)
    no longer disappears silently from the tool list: it surfaces the same
    `<connector>__authenticate` tool worded as a reconnect, and calling any of
    its tools returns a reconnect link — so a stale credential is recoverable
    from the client.
  * The Groups & Policies page now shows the correct name and icon for grants on
    disabled connectors and on admin built-in tools, and the grant editor lists
    them, so a grant no longer becomes anonymous or uneditable when its
    connector is switched off or the viewing admin lacks the instance-admin
    role.

  [Compare v0.2.1...v0.2.2](https://github.com/PipedreamHQ/conduit/compare/v0.2.1...v0.2.2)
</Update>

<Update label="v0.2.1" description="2026-07-20">
  *Pipedream connector tools fix, MCP tool metadata, faster restarts.*

  ## Upgrade notes

  None.

  ## Changed

  * Built-in Conduit tools now declare MCP behavior-hint annotations
    (`readOnlyHint`, `destructiveHint`, `idempotentHint`), so clients can
    auto-approve read-only calls like `list_connectors` and warn before
    destructive ones like `admin_delete_workspace`. Annotations from upstream
    MCP servers pass through to clients unchanged.
  * Built-in Conduit tools now carry a human-readable display title (e.g.
    "Join Workspace"), which MCP clients that support tool titles show in
    place of the wire name. Tool titles from upstream MCP servers are now
    passed through to clients as well.
  * Shutdown now ends open MCP SSE streams immediately instead of waiting out
    the 10-second drain timeout on connections that never finish on their own,
    so a stop or redeploy completes in under a second once in-flight requests
    drain. MCP clients reconnect automatically.

  ## Fixed

  * Pipedream app tools now appear on `/mcp` in workspaces whose access policy
    grants all connectors with a wildcard (including the default policy every
    workspace starts with). The gateway told Pipedream which apps to serve only
    when the policy listed them explicitly, so a wildcard grant surfaced no app
    tools at all — enabling an app on the Connectors page had no visible effect.
    It now always names the user's enabled apps to Pipedream.
  * MCP clients that sat idle long enough for all their tokens to expire no
    longer lose their OAuth client registration to the cleanup sweep — a
    returning client (e.g. Claude Code reusing its cached `client_id`) failed
    every authorization with "invalid client\_id" and had to be manually
    re-registered. Only abandoned registrations that never completed an
    authorization are reclaimed now.
  * Failed `/oauth/authorize` requests (unknown `client_id`, unregistered
    `redirect_uri`) are now logged server-side; previously the only signal was
    the error page in the end user's browser.
  * On the embedded database, restarting or redeploying an instance no longer
    trips the single-instance guard. The guard treated the previous process's
    last heartbeat (up to 90 seconds old) as a live second instance, so a
    normal stop-then-start deploy crash-looped for a few minutes before coming
    up. It now flags only instances that write a heartbeat after the current
    process started — a real concurrent writer is still detected within one
    30-second beat.

  [Compare v0.2.0...v0.2.1](https://github.com/PipedreamHQ/conduit/compare/v0.2.0...v0.2.1)
</Update>

<Update label="v0.2.0" description="2026-07-19">
  *PostgreSQL support and active-active high availability.*

  ## Upgrade notes

  None — database migrations apply automatically on boot. The new
  PostgreSQL/high-availability features are opt-in via `CONDUIT_DATABASE_URL`;
  embedded deployments are unchanged.

  ## Added

  * PostgreSQL support: set `CONDUIT_DATABASE_URL` to run Conduit on an
    operator-supplied PostgreSQL (14+) instead of the embedded database. The
    embedded zero-dependency default is unchanged. Requires
    `CONDUIT_ENCRYPTION_KEY` (and `CONDUIT_ADMIN_PASSWORD` until setup
    completes); usage metrics live in the same database.
  * High availability: on PostgreSQL, Conduit runs active-active with any
    number of replicas behind a load balancer — zero-downtime rolling
    upgrades, live cache invalidation between replicas, cluster-wide
    authentication-failure throttling, and a `/healthz/ready` readiness
    endpoint that checks database connectivity. See the High Availability
    deployment guide.
  * Conduit now refuses to start when another live instance is using the same
    embedded database file — a misconfigured multi-instance deployment fails
    loudly instead of corrupting data.
  * Deployment guides for Kubernetes, high availability and backups, and
    other platforms (Compose, VMs, Fly.io).

  ## Fixed

  * The first-run setup wizard survives a server restart between signing in
    with the bootstrap credential and completing setup.
  * Connecting a connector account via OAuth survives a server restart
    mid-flow (between opening the provider's consent page and returning).

  [Compare v0.1.0...v0.2.0](https://github.com/PipedreamHQ/conduit/compare/v0.1.0...v0.2.0)
</Update>

<Update label="v0.1.0" description="2026-07-19">
  *The first tagged release of Conduit.*

  ## Upgrade notes

  None — this is the first release.

  ## Added

  * **MCP gateway**: tools from MCP servers, GraphQL and OpenAPI connectors,
    and the Pipedream connector catalog, aggregated behind a single governed
    MCP endpoint with OAuth 2.1 and dynamic client registration.
  * **Access policies**: allow policies and groups decide who can use which
    connectors and tools, enforced on every tool listing and call; users
    refine their own enabled set. Connected clients refresh live when access
    changes.
  * **Workspaces**: single-workspace self-host mode and multi-workspace
    (tenant) mode, with per-workspace connector configuration, join policies,
    and provisioning rules.
  * **Authentication**: email + password and identity providers (social
    presets and custom OIDC), workspace SSO, and an instance auth policy.
  * **Observability**: audit log of admin changes and sign-ins, OpenTelemetry
    traces and events, and usage dashboards.
  * **Built-in documentation**: this docs site ships inside the Conduit image,
    served at `/docs/` and always matching the running version; the app
    sidebar links to the docs and to the running version's changelog.

  [Full commit history](https://github.com/PipedreamHQ/conduit/commits/v0.1.0)
</Update>
