Files
windmill/backend/migrations/20260919213810_remote_deploy.up.sql
Ruben FiszelandClaude Opus 5 4d12ea4614 feat: deploy from the UI to a workspace on another instance (#11245)
* feat: deploy from the UI to a workspace on another instance

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: keep the remote deploy proxy from being spent by a link

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: key remote deploy proxy URLs instead of a global client header

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: keep the remote deploy proxy key out of logs and restricted hands

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: serialize remote deploy connect with account and key changes

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat: key remote deploy tokens to the account and connect by signing in

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: bind remote deploy connect to its target and order its locks

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: serialize remote deploy connect with target changes

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: list every lock remote deploy connect takes in auth-surface

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: never wait on the membership lock in remote deploy connect

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: keep a superseded target response out of the settings form

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: void stale remote deploy tokens on read instead of locking in connect

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: void remote deploy tokens older than the last target change

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: order remote deploy connections by when their connect started

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: bind stored remote deploy tokens to the membership and target they were connected under

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: answer remote deploy connect without settings as no target

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 01:40:53 +02:00

36 lines
2.1 KiB
SQL

-- The workspace on another Windmill instance this workspace deploys into from the UI:
-- `{"base_url": ..., "workspace_id": ...}`, and when it last changed. A token connected under an
-- earlier change counts for nothing, even once the setting points back at the target it was for.
ALTER TABLE workspace_settings
ADD COLUMN remote_deploy_target JSONB,
ADD COLUMN remote_deploy_target_changed_at TIMESTAMPTZ;
-- One user's token for the remote deploy target, encrypted with the workspace key.
-- `base_url`/`remote_workspace_id` name the target it was granted for: a token is only
-- ever sent to that target, so re-pointing the workspace setting cannot redirect it.
-- Keyed by the account rather than by membership, since a superadmin deploys from workspaces
-- it is not a member of. The account key cascades: whatever deletes or renames an account
-- takes its tokens along, so a later account with the same address cannot inherit them.
CREATE TABLE remote_deploy_token (
workspace_id VARCHAR(50) NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
email VARCHAR(255) NOT NULL REFERENCES password(email) ON DELETE CASCADE ON UPDATE CASCADE,
base_url VARCHAR(1000) NOT NULL,
remote_workspace_id VARCHAR(50) NOT NULL,
token TEXT NOT NULL,
remote_email VARCHAR(255) NOT NULL,
-- Part of every proxy URL, and readable only by its owner through the API: a link from
-- elsewhere, which rides the session cookie, cannot know it and so cannot spend the token.
proxy_key VARCHAR(64) NOT NULL,
-- When the connect that wrote the row started, or when a disconnect emptied it.
connected_at TIMESTAMPTZ NOT NULL DEFAULT now(),
-- What the connect ran under: the `created_at` of the owner's membership (NULL for a
-- superadmin with none) and the target's `remote_deploy_target_changed_at`. The row counts
-- only while both are still the same, so a re-add or a target change voids it.
member_since TIMESTAMPTZ,
target_changed_at TIMESTAMPTZ,
PRIMARY KEY (workspace_id, email)
);
GRANT ALL ON remote_deploy_token TO windmill_user;
GRANT ALL ON remote_deploy_token TO windmill_admin;