Skip to content

feat(lk): confirm a member removal that deletes API keys - #1079

Merged
Topherhindman merged 6 commits into
mainfrom
topherhindman/devx-890-lk-confirms-a-member-removal-when-the-member-owns-api-keys
Oct 27, 2026
Merged

Topherhindman merged 6 commits into
mainfrom
topherhindman/devx-890-lk-confirms-a-member-removal-when-the-member-owns-api-keys

Conversation

@Topherhindman

Copy link
Copy Markdown
Contributor

Removing a member who owns API keys is now a two-step call: the API refuses with a MemberRemovalNotConfirmed detail (the key count, and any projects the removal would leave without a key), and the caller retries with confirmedNumKeys.

  • lk project member remove and lk workspace member delete ask before deleting the member's keys, then retry with the count. --yes confirms without asking; a run that can't prompt without --yes fails and says so, so keys are never deleted silently.
  • A removal that would leave a project without an API key is refused, and lk names the projects.
  • If the count changes before the retry, lk asks once more, then fails.
  • --json output adds numKeys.
  • lk project member remove says in its help and prompt that, unless the project is private, the removal reaches the whole workspace.

Unit tests and four script tests cover no keys, confirmed keys, a count that changes, and the last-key block.

Linear: DEVX-890

Picks up livekit/public-api-server#97. ProjectService.RemoveMember and
WorkspaceService.DeleteMember take a confirmedNumKeys query parameter,
and their responses report numKeys, the API keys the removal deleted.
The client methods pass no parameters yet, so lk sends what it sent
before.

Generated from livekit/public-api-server@7bfa9ec
RemoveProjectMember and DeleteWorkspaceMember take confirmedNumKeys and
return how many API keys the removal deleted. When the member owns keys
the caller didn't confirm, or the removal would take a project's last
key, the Public API refuses with a MemberRemovalNotConfirmed detail;
MemberRemovalNotConfirmed reads it from the error.

A count of 0 is left out of the request, which the API reads as 0. A
server from before livekit/public-api-server#97 answers the parameter
with a 500 (unknown field), so leaving it out keeps removals working
there until #97 is deployed.

The commands still confirm 0, so they send what they sent before.
lk project member remove and lk workspace member delete first ask the
Public API to remove the member with no keys confirmed. When the member
owns API keys, the API refuses and says how many; lk asks the user to
confirm deleting them and retries with that count, then says how many
keys the removal deleted (numKeys in --json).

--yes confirms without asking. A run that can't prompt fails and says
to re-run with --yes, rather than deleting keys unasked as
confirmDestroy would.

A removal that would leave a project without an API key is refused even
when confirmed, so lk names the projects (and counts the private ones)
and doesn't offer to confirm. If the count changes before the retry, lk
asks once more with the new count, and fails if it changes again.
Four scripts cover a member who owns no keys, keys confirmed with --yes
(and refused without it when lk can't prompt), the count changing
between calls, and a removal that would take a project's last key.

The fake cloud answered a URL whatever its query, so it couldn't tell
lk's first call from its retry. A respond URL with a query now answers
only that query and wins over one without, so each retry gets the
answer for the count it confirms, and a wrong count gets the first
call's refusal again. The two member removal routes join
publicAPIRoutes, so their canned bodies are checked against the
generated types.
A project that isn't private shares its members with its workspace, so
the Public API removes the member from the workspace and every project
in it, and the key count covers all of them. lk project member remove
now says so in its help and in the confirmation prompt.
If the member's keys are revoked between lk's calls, the retry is
refused with a count of 0; lk now retries with 0 instead of asking to
delete 0 keys. Ctrl-C at the prompt prints "aborted", as the other
destructive commands do. A run that can't prompt now says that a project
removal reaches the whole workspace, as the prompt does, and says to
re-run with --yes "to go ahead".

The no-keys script answers an explicit confirmedNumKeys=0 with the 500 a
server from before the count sends, so it fails if lk stops leaving the
0 out.

@rektdeckard rektdeckard left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although I like the idea of cleaning up resources, I don't think this is safe for us to make this decision. Self-hosted agents may be deployed with keys created by a user, who, upon removal, would break the agent. I think the appropriate step to take would actually be server-side, disowning the API keys from that user.

@rektdeckard

Copy link
Copy Markdown
Member

Approving for now, I believe in future we should support two different user deletion behaviors on the server:

  • Nuke user and all they owned data including API keys
  • Delete the user but disown or reparent their resources (including API keys)

@Topherhindman
Topherhindman merged commit 72d421d into main Oct 27, 2026
26 checks passed
@Topherhindman
Topherhindman deleted the topherhindman/devx-890-lk-confirms-a-member-removal-when-the-member-owns-api-keys branch October 27, 2026 21:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants