From b309454d0c0408737cc07caf679464d35d84a874 Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Thu, 27 Aug 2026 09:37:04 +0700 Subject: [PATCH 1/2] [Dashboard] Send the vault rotation mode when rotating an admin key The rotate endpoint now takes `mode: "managed" | "ejected"` alongside the optional project secret key, so the caller states the vault state it wants rather than the server inferring it from what is already stored. Unticking "Keep this vault managed" previously sent only an absent secret key, which the server read as a managed vault missing its key. `willStayManaged` already drives the dialog copy and the submit button; it now also picks the mode. Ejecting sends no secret key, since there is nothing to re-encrypt. No UX change: an ejected result still shows both credentials with the .txt download and the confirmation gate on close, and a managed result still shows the mask. Co-authored-by: Claude Fable 5 --- apps/dashboard/src/@/actions/vault.ts | 14 +++++++++++++- .../vault/components/rotate-admin-key.client.tsx | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/dashboard/src/@/actions/vault.ts b/apps/dashboard/src/@/actions/vault.ts index 76421f45c9b..c2c2120f439 100644 --- a/apps/dashboard/src/@/actions/vault.ts +++ b/apps/dashboard/src/@/actions/vault.ts @@ -190,14 +190,26 @@ export async function createVaultServiceAccount(params: { return res.data.data; } +/** + * Rotates the project's vault admin key. + * + * `mode` is the state to leave the vault in. `managed` re-seals the new + * credentials with the project secret key and returns only the mask; `ejected` + * returns the admin key and wallet token once and keeps no copy. Omitting it + * keeps the vault as it is. + */ export async function rotateVaultServiceAccount(params: { project: ProjectRef; + mode?: "managed" | "ejected"; projectSecretKey?: string; }): Promise { const res = await apiServerProxy<{ data: RotateVaultServiceAccountResult; }>({ - body: JSON.stringify({ projectSecretKey: params.projectSecretKey }), + body: JSON.stringify({ + mode: params.mode, + projectSecretKey: params.projectSecretKey, + }), headers: { "Content-Type": "application/json" }, method: "POST", pathname: vaultPath(params.project, "/service-account/rotate"), diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/rotate-admin-key.client.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/rotate-admin-key.client.tsx index dcc8b831f23..e129d006dcb 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/rotate-admin-key.client.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/rotate-admin-key.client.tsx @@ -48,6 +48,7 @@ export default function RotateAdminKeyButton(props: { const rotateAdminKeyMutation = useMutation({ mutationFn: async () => { return rotateVaultServiceAccount({ + mode: willStayManaged ? "managed" : "ejected", project: { projectId: props.project.id, teamId: props.project.teamId, From 96cd13e40084635d28feb6dc83a25a4c7ee72c5d Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Thu, 27 Aug 2026 10:12:40 +0700 Subject: [PATCH 2/2] [Dashboard] Derive the rotation mode from the checkbox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mode was computed from `willStayManaged`, which also requires the secret key field to be non-empty. Ticking "keep managed" without having typed the key yet therefore resolved to "ejected" — an irreversible outcome derived from form completeness rather than intent. Co-Authored-By: Claude Fable 5 --- .../(sidebar)/vault/components/rotate-admin-key.client.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/rotate-admin-key.client.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/rotate-admin-key.client.tsx index e129d006dcb..4dbe75513ac 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/rotate-admin-key.client.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/rotate-admin-key.client.tsx @@ -48,7 +48,7 @@ export default function RotateAdminKeyButton(props: { const rotateAdminKeyMutation = useMutation({ mutationFn: async () => { return rotateVaultServiceAccount({ - mode: willStayManaged ? "managed" : "ejected", + mode: stayManaged ? "managed" : "ejected", project: { projectId: props.project.id, teamId: props.project.teamId,