Bruno V4 - Release Notes

Download

Bruno V4 changes how secrets are stored and how environment variables behave. It also adds new optional fields to requests and environments like descriptions and variable types so their schema changes slightly.

Some of these changes require action before upgrading; others are just good to know if your team works across mixed Bruno versions.

Have feedback? Join the discussion on GitHub.

⚠️ Breaking Changes: V4 includes breaking changes to secret storage, environment variable persistence, WebSocket message format, and CLI JUnit output. Please review the sections below before upgrading.

Secret Manager configuration is moving into your environments

Action required for CLI / CI users

What's changing

In V4, external secrets configuration no longer lives in a secrets.json file at the root of your collection. It moves into your environment files, under a new externalSecrets section, and is managed from the Environment UI instead of from Collection Settings > Secrets.

You also get a simpler way to reference a secret - by its name directly:

SyntaxStatus
{{name.keyname}}New (recommended)
{{$secrets.name.keyname}}Old (still works, deprecated)

Both resolve to the same value for now, so nothing breaks immediately. The old {{$secrets.*}} form is deprecated and will be removed after 3 months. Wherever the old syntax appears, the app marks it with an underline and a hover tooltip pointing you to the new form.

Why we're doing this

Secrets are environment-specific - your Production token is not your Staging token. Keeping configuration alongside the environment it belongs to is clearer, removes a separate root-level file, and makes the environment the single source of truth. It also brings full secret manager support to the CLI across AWS, Azure, and Vault, and adds support for workspace and global environments, which the old secrets.json model could not express.

What you need to do

Bruno app (GUI) - nothing required

Migration is automatic. When you open a collection in V4, Bruno moves your secret configuration from secrets.json into the relevant environment files in the background, then deletes secrets.json once migration succeeds. Your secret values are never written to disk - only the mappings and provider configuration move.

CLI / CI - action required

The CLI does not migrate on its own. If your collection still uses a secrets.json, your run will show a warning to open the collection in the Bruno app and go to Collection Settings > Secrets to complete migration. The warning does not block the run. Once you've migrated in the app, pass the environment to your CLI run: bru run --env <name>

Before and after

Before - secrets.json at collection root

{
  "type": "aws-secrets-manager",
  "data": [
    {
      "environment": "Production",
      "secrets": [
        { "name": "dbPassword", "secretName": "prod/db/credentials", "enabled": true },
        { "name": "apiKey", "secretName": "prod/payment-gateway/api-key", "enabled": true }
      ]
    }
  ]
}

After - YAML: environments/production.yml

name: Production
variables:
  - name: baseUrl
    value: https://api.example.com
externalSecrets:
  type: aws-secrets-manager
  variables:
    - name: dbPassword
      secretName: prod/db/credentials
      enabled: true
    - name: apiKey
      secretName: prod/payment-gateway/api-key
      enabled: true

After - BRU: environments/staging.bru

vars {
  baseUrl: https://staging.api.example.com
}
vars:externalsecrets:aws-secrets-manager {
  dbPassword: staging/db/credentials
  ~apiKey: staging/payment-gateway/api-key
}

Environment variable changes are saved to disk by default

Action required if scripts set sensitive values

What's changing

In V4, scripts that modify environment variables automatically persist those changes to disk. This makes script-driven variable updates consistent and predictable.

Watch out: If your scripts set tokens, credentials, or other sensitive values with bru.setEnvVar(), bru.deleteEnvVar(), or bru.setGlobalEnvVar(), those values will now be written to your environment file and could be committed to version control by accident.

Who is affected

You are affected if:

  • Your scripts call bru.setEnvVar(), bru.deleteEnvVar(), or bru.setGlobalEnvVar() - these did not write to disk before V4, and now they do
  • Any of those calls handle sensitive values such as access tokens, refresh tokens, API keys, or passwords
  • You commit your environment files to version control, where a newly persisted secret could be pushed to a shared repository

You are not affected if:

  • Your scripts only set non-sensitive values (e.g. a baseUrl, a counter, or a request ID)
  • You don't modify environment variables from scripts at all

How to prepare

  1. Audit your scripts for bru.setEnvVar(), bru.deleteEnvVar(), and bru.setGlobalEnvVar() usage that handles sensitive values
  2. For sensitive values, switch those calls to bru.setVar() / bru.deleteVar(), which keep the value in memory for the run instead of persisting it
  3. In V3.5.0+, enable the V4 migration option in the footer panel - it highlights these API calls in the sidebar, scripts, and tests so you can find and update them before upgrading

Multiple WebSocket messages

No migration needed - watch out on mixed-version teams

What's new

In V4, you can add multiple WebSocket messages to a single request. To support this, Bruno saves requests in a new message format. Existing requests keep working, and you can now define more than one message per WebSocket request.

Mixed-version teams: Collections using the new format may not load correctly in Bruno V3.5.0 or earlier. This affects only YAML collections with WebSocket requests - BRU format is not impacted.

Schema changes

V3.5.0 - BRU (single message)

body:ws {
  type: <json | text>
  content: '''
    <message payload>
  '''
}

V4 - BRU (multiple messages)

body:ws {
  name: <message title>
  type: <json | text>
  selected: <true | false>
  content: '''
    <message payload>
  '''
}

V3.5.0 - YAML

message:
  type: <json | text>
  data: "<message payload>"

V4 - YAML

message:
  - title: <message title>
    selected: <true | false>
    message:
      type: <json | text>
      data: |-
        <message payload>

Descriptions and variable types

No migration needed - watch out on mixed-version teams

What's new

In V4, you can add descriptions to params, headers, multipart form fields, and variables, and assign types (@string, @number, @boolean, @object) to your variables. Bruno stores these as annotations on the line above each pair.

How older versions handle it:

  • V3.4.0 to pre-V4: Collections open fine, but descriptions won't appear and variables won't get type handling
  • Below V3.4.0: The older parser doesn't understand the annotation syntax, so affected requests may fail to load and environments may appear missing

Schema changes

Pre-V4 - BRU

headers {
  Content-Type: application/json
  Authorization: Bearer {{token}}
}
params:query {
  page: 1
}
vars:pre-request {
  baseUrl: https://api.example.com
  retryCount: 3
}

V4 - BRU

headers {
  @description('Payload format for the request')
  Content-Type: application/json
  @description('Bearer token used for auth')
  Authorization: Bearer {{token}}
}
params:query {
  @description('Page number to fetch')
  page: 1
}
vars:pre-request {
  @string
  @description('Base URL for all requests')
  baseUrl: https://api.example.com
  @number
  retryCount: 3
}

Pre-V4 - YAML

http:
  headers:
    - name: Content-Type
      value: application/json
    - name: Authorization
      value: Bearer {{token}}
  params:
    - name: page
      value: "1"
      type: query
runtime:
  variables:
    - name: baseUrl
      value: https://api.example.com
    - name: retryCount
      value: "3"

V4 - YAML

http:
  headers:
    - name: Content-Type
      value: application/json
      description: Payload format for the request
    - name: Authorization
      value: Bearer {{token}}
      description: Bearer token used for auth
  params:
    - name: page
      value: "1"
      type: query
      description: Page number to fetch
runtime:
  variables:
    - name: baseUrl
      value: https://api.example.com
      description: Base URL for all requests
    - name: retryCount
      value:
        type: number
        data: "3"

CLI JUnit report: classname now uses the request path

Action required if you consume CLI JUnit output in CI

What's changing

In the CLI JUnit reporter, each test case's classname attribute changes from the request URL to the collection path to the request - for example, Users/Get Users for a request named "Get Users" inside a "Users" folder.

Why we're doing this

CI tools like Xray and Azure DevOps use classname + name as a test's unique identifier. Because the URL changes per environment, the same test produced a different classname in staging vs. production and showed up as duplicate test cases. The collection path is stable across environments.

Who is affected

  • Teams that feed the CLI JUnit report into Xray, Azure DevOps, or similar tools
  • Anyone with scripts that read classname expecting a URL

Before and after

Before - URL (varies per environment)

<testcase
  name="Status is 200"
  classname="https://prod.api.example.com/users"
/>

After - collection path (stable)

<testcase
  name="Status is 200"
  classname="Users/Get Users"
/>

Before you upgrade ✅

  • Audit bru.setEnvVar(), bru.deleteEnvVar(), and bru.setGlobalEnvVar() usage for sensitive values, and move those to bru.setVar() / bru.deleteVar()
  • Coordinate the upgrade to V4 across mixed-version teams - saving a collection in V4 can break it for teammates on older versions (descriptions/types, secret config, WebSocket messages)
  • If your CI tooling reads classname from the CLI JUnit report and expects a URL, update it to expect the collection path (Folder/Request Name) instead

Once on V4

  • If your collection uses an external secret manager, open it in the app so the secret config migrates out of secrets.json, then go to Collection Settings > Secrets > Go to Environment to verify
  • If you run from the CLI, complete the above step in the app first before running that collection in V4
  • Update deprecated {{$secrets.*}} references to {{name.keyname}} when convenient (within the 3-month window)

Ready to upgrade to Bruno V4?

Download the latest version and enjoy all the new features and improvements.

Download Bruno V4

Have questions or feedback? Join the discussion on GitHub