Skip to content

feat(workspace): bl workspaces hipaa accept|decline|status + --accept-not-hipaa on deploy/apply#299

Open
devin-ai-integration[bot] wants to merge 3 commits into
mainfrom
devin/1778707061-cli-hipaa
Open

feat(workspace): bl workspaces hipaa accept|decline|status + --accept-not-hipaa on deploy/apply#299
devin-ai-integration[bot] wants to merge 3 commits into
mainfrom
devin/1778707061-cli-hipaa

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot commented May 13, 2026

Summary

CLI complement to the HIPAA opt-in feature landing in controlplane#3941. Adds three subcommands under bl workspaces hipaa:

Command Behaviour
bl workspaces hipaa accept PUT /workspaces/{name}/hipaa with hipaaOptIn=true
bl workspaces hipaa decline PUT /workspaces/{name}/hipaa with hipaaOptIn=false
bl workspaces hipaa status GET /workspaces/{name}, prints accepted / declined
  • accept and decline prompt with [y/N] on an interactive TTY; pass -y / --yes (or run without a TTY plus --yes) to skip the prompt in CI.
  • The global -w/--workspace flag still targets a non-current workspace.
  • sdk-go v0.18.0 doesn't ship a typed HipaaOptIn field on Workspace yet, so the commands use the generic client.Get / client.Put directly against the new controlplane endpoint and decode the response into a small local struct. When the SDK is regenerated post-merge, this code keeps working with no changes required.
  • Deploy errors require no CLI changes. bl deploy / bl apply already pipe controlplane 4xx responses through extractErrorMessage (cli/apply.go), which reads the error field on the JSON body — exactly the field the HIPAA validation in controlplane#3941 populates. The CP message ("HIPAA compliance is required to deploy …") will surface verbatim in the deploy failure line.

Review & Testing Checklist for Human

  • bl workspaces hipaa accept on a workspace whose admin has not previously toggled the flag — confirm the workspace settings UI in app reflects the new value after the call.
  • bl workspaces hipaa accept --yes from a non-TTY (e.g. piped) succeeds; without --yes it should refuse with a clear stderr message.
  • bl deploy against a HIPAA-gated region with hipaaOptIn=false — verify the failure line shows the controlplane HIPAA error (not a generic 400). This is exercised by the same regions configured in controlplane#3941's hipaaBlocking.products map.
  • Non-admin user tries bl workspaces hipaa accept — should surface the controlplane's 403/permission error verbatim.

Notes

  • Companion PR (controlplane backend + UI): https://github.com/blaxel-ai/controlplane/pull/3941
  • sdk-go is unchanged. Once stainless regenerates with the new endpoint, this command can switch to a typed client.Workspaces.UpdateHipaa(...) if desired, but the current generic-client approach has no migration cost.

Link to Devin session: https://app.devin.ai/sessions/485196f78a1e4fd19e97919a72ac3bd1
Requested by: @Joffref


Note

Adds bl workspaces hipaa accept|decline|status subcommands to manage workspace-level HIPAA consent (hipaaUnsafe flag). Also adds --accept-not-hipaa to bl deploy/bl apply for per-invocation consent via an X-Blaxel-Acknowledge-Not-Hipaa header. Uses the generic HTTP client with a local struct to work around sdk-go not yet exposing the new field.

Written by Mendral for commit ba9bce9.


Open in Devin Review

Adds three subcommands under 'bl workspaces hipaa' to manage the new
workspace HIPAA opt-in flag from the CLI:

  - accept   PUT /workspaces/{name}/hipaa with hipaaOptIn=true
  - decline  PUT /workspaces/{name}/hipaa with hipaaOptIn=false
  - status   GET /workspaces/{name} to print current state

accept/decline prompt for y/N on a TTY; pass -y/--yes (or run with a
non-TTY stdin and --yes) to skip the prompt for CI. The --workspace
global flag targets a non-current workspace.

bl deploy / bl apply already surface the controlplane's HIPAA error
verbatim via extractErrorMessage ("error" field), so no changes are
needed there.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

mendral-app[bot]

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment thread cli/workspace.go
Comment on lines +225 to +237
workspaceName := resolveWorkspaceName()

if !assumeYes && !confirmHipaaChange(workspaceName, optIn) {
core.Print("Aborted.\n")
return
}

client := core.GetClient()
if client == nil {
err := fmt.Errorf("no API client available. Please run 'bl login' first")
core.PrintError("Workspace", err)
core.ExitWithError(err)
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🟡 Interactive confirmation prompt happens before client availability check

In runWorkspaceHipaaUpdate, the user is prompted for interactive confirmation (line 227) before the API client availability check (line 232-237). If the user is not logged in (core.GetClient() returns nil), they will go through the full TTY confirmation dialog, type "y", and only then be told they need to run bl login. The cheap, non-interactive client check should precede the interactive prompt to avoid wasting the user's time on an operation that is guaranteed to fail.

Suggested change
workspaceName := resolveWorkspaceName()
if !assumeYes && !confirmHipaaChange(workspaceName, optIn) {
core.Print("Aborted.\n")
return
}
client := core.GetClient()
if client == nil {
err := fmt.Errorf("no API client available. Please run 'bl login' first")
core.PrintError("Workspace", err)
core.ExitWithError(err)
}
workspaceName := resolveWorkspaceName()
client := core.GetClient()
if client == nil {
err := fmt.Errorf("no API client available. Please run 'bl login' first")
core.PrintError("Workspace", err)
core.ExitWithError(err)
}
if !assumeYes && !confirmHipaaChange(workspaceName, optIn) {
core.Print("Aborted.\n")
return
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch — fixed in 83d59b7. Moved the core.GetClient() nil-check above the TTY confirmation so an unauthenticated user gets the bl login message immediately instead of after typing through the y/N dialog.

Move the cheap core.GetClient() nil-check above the y/N TTY prompt so
that an unauthenticated user is told to run 'bl login' immediately
rather than after typing through the entire confirmation dialog.

Reported by Devin Review on PR #299.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Aligns the CLI with controlplane PR #3941 (post semantic-flip):

  - 'bl workspaces hipaa accept'  now grants STANDING consent to deploy
    in non-HIPAA-compliant regions/products (workspace.hipaaUnsafe=true).
  - 'bl workspaces hipaa decline' revokes that standing consent
    (workspace.hipaaUnsafe=false).
  - 'bl workspaces hipaa status'  reports whether non-HIPAA-compliant
    deploys are allowed or blocked for the workspace.

The PUT body and GET response are renamed to 'hipaaUnsafe' to match the
new controlplane field.

Adds '--accept-not-hipaa' to 'bl deploy' and 'bl apply'. When set, every
resource upsert routed through handleResourceOperation sends the
X-Blaxel-Acknowledge-Not-Hipaa: true header so the controlplane permits
a single non-HIPAA-compliant deploy even when workspace.hipaaUnsafe is
false. The flag is process-scoped (set once per invocation) and applies
to every resource in the apply / deploy batch.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@mendral-app mendral-app Bot left a comment

Choose a reason for hiding this comment

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

LGTM

Previous comment (client nil-check before prompt) was addressed in 83d59b7. The semantic flip and --accept-not-hipaa flag in ba9bce9 are correctly implemented — package-level process-scoped state is appropriate for a CLI, header injection is in the right place, and the confirmation prompt logic is sound. No correctness, security, or data-loss issues.

Tag @mendral-app with feedback or questions. View session

@devin-ai-integration devin-ai-integration Bot changed the title feat(workspace): add bl workspaces hipaa accept|decline|status feat(workspace): bl workspaces hipaa accept|decline|status + --accept-not-hipaa on deploy/apply May 13, 2026
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.

1 participant