Skip to content

Commit f84e954

Browse files
committed
Freeze organizations moving to v2
Writes, OAuth completion and OAuth token refresh are refused for an organization whose org_moved_to_v2 row is moving or moved (423 Locked; 503 when the state cannot be read). The dashboard of a moved organization redirects to its v2 page. Toolkit connections forwarded to v2 are now named per member. Migration 0022 adds v2_org_slug and the rolled_back state, and grants v1_import_writer its access when the role exists. For a role created after the migration has run: GRANT INSERT, UPDATE ON org_moved_to_v2 TO v1_import_writer; GRANT SELECT (org_id, state, v2_org_id, v2_org_slug) ON org_moved_to_v2 TO v1_import_writer;
1 parent 4998ad0 commit f84e954

28 files changed

Lines changed: 2766 additions & 85 deletions

‎.changeset/v1-move-freeze.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@executor-js/sdk": patch
3+
"@executor-js/api": patch
4+
"@executor-js/cloud": patch
5+
---
6+
7+
Hosts can pause every change to a workspace with `workspaceWriteGate`: connection and OAuth writes, OAuth completion (before the code is exchanged) and OAuth token refreshes are refused with the host's message, while reads and tool calls keep working. The OAuth callback popup now shows why a refusal the user can act on happened. Executor Cloud uses this to freeze an organization while it moves to Executor v2, refuses changes to it once it has moved, and opens a moved organization's dashboard in v2.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-- `org_moved_to_v2` gains the v2 organization's slug (v2 writes it with
2+
-- `moved`; the v1 dashboard redirects to `/org/<slug>` on v2) and a third
3+
-- state, `rolled_back` (v2 gave the organization back; v1 serves it as if
4+
-- there were no row). The constraint swap is safe online: the table holds at
5+
-- most one row per organization that started a move.
6+
ALTER TABLE "org_moved_to_v2" DROP CONSTRAINT "org_moved_to_v2_state_check";--> statement-breakpoint
7+
ALTER TABLE "org_moved_to_v2" ADD COLUMN "v2_org_slug" text;--> statement-breakpoint
8+
ALTER TABLE "org_moved_to_v2" ADD CONSTRAINT "org_moved_to_v2_state_check" CHECK ("org_moved_to_v2"."state" in ('moving', 'moved', 'rolled_back'));--> statement-breakpoint
9+
-- v2 writes this table as `v1_import_writer`, with
10+
-- INSERT INTO org_moved_to_v2 (org_id, state, v2_org_id, v2_org_slug) VALUES (...)
11+
-- ON CONFLICT (org_id) DO UPDATE SET state = excluded.state,
12+
-- v2_org_id = excluded.v2_org_id, v2_org_slug = excluded.v2_org_slug
13+
-- which needs INSERT and UPDATE on the table and SELECT on the columns the
14+
-- upsert names (the conflict target and the `excluded` reads). It gets nothing
15+
-- else: no DELETE, no SELECT of `updated_at`, nothing on any other table.
16+
--
17+
-- The grant runs only when the role already exists. When the role is created
18+
-- after this migration has run, run these two statements once, after
19+
-- `CREATE ROLE v1_import_writer ...`:
20+
-- GRANT INSERT, UPDATE ON org_moved_to_v2 TO v1_import_writer;
21+
-- GRANT SELECT (org_id, state, v2_org_id, v2_org_slug) ON org_moved_to_v2 TO v1_import_writer;
22+
DO $$
23+
BEGIN
24+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'v1_import_writer') THEN
25+
GRANT INSERT, UPDATE ON org_moved_to_v2 TO v1_import_writer;
26+
GRANT SELECT (org_id, state, v2_org_id, v2_org_slug) ON org_moved_to_v2 TO v1_import_writer;
27+
END IF;
28+
END $$;

0 commit comments

Comments
 (0)