Skip to content

Project folder format

A project is a folder, not one file. Every file inside it is UTF-8 text with a stable key order, and each file holds exactly one concept — a manifest, an interface, one request. That is deliberate: two people editing different requests touch different files, a one-line change produces a one-line diff, and the folder can be reviewed, grepped and diffed without Wirebench installed.

my-service/
wirebench.yaml # manifest: id, name, settings, properties, environments
environments/dev.yaml # endpoints + properties for one environment
interfaces/<Interface>/
interface.yaml # definitionUrl, cache, soapVersion, endpoints, auth (refs only)
definition/ # the fetched WSDL/XSD, byte-exact, plus manifest.yaml (url → file, sha256)
operations/<Operation>/
<Request>.request.yaml # endpointRef, headers, attachments, auth, WS-Addressing, properties
<Request>.xml # the SOAP envelope, exactly as edited
<Request>.pre.ts # a pre-request script, when the request has one (.post.ts after the response)
apis/<Api>/
api.yaml # kind: rest, baseUrl, auth (refs only), settings, definition ref
definition/ # the imported OpenAPI document, byte-exact, plus manifest.yaml
requests/
<Request>.request.yaml # kind: rest, method, url, path params, query, headers, body, auth
<Request>.body.json # a raw body, in a file of its own language (.json/.xml/.txt/…)
<Request>.examples/ # the request's response examples, one <id>.body.<ext> each (format 7)
<Request>.pre.ts # the request's scripts, as for SOAP (.js when imported from Postman)
<Request>.golden.yaml # the request's snapshot, when one is saved (SOAP requests have one too)
<Folder>/folder.yaml # a folder's own name, order and inherited auth
sequences/
<Sequence>.sequence.yaml # kind: sequence, version, steps (request ids), transfers, assertions
mocks/<Mock>/
mock.yaml # kind: mock, version, source (interface or API id), port, path, validation
operations/<Operation>/
operation.yaml # the contract operation's key, dispatch style, default response id
<Response>.response.yaml # status, headers, delay, match conditions, scenario state
<Response>.body.xml # the response body, sent byte for byte (.json/.txt by its language)
dispatch.ts # the dispatch script, only when the operation dispatches by script
wss/… # outgoing/incoming WS-Security configs and keystore entries (no secrets)
attachments/ # content-addressed by sha256

A SOAP envelope is a plain .xml file, never a string embedded in YAML, so it edits and diffs as XML. A REST body is the same idea: it lives beside its request as <Request>.body.json (or .xml/.txt, matching its content) rather than as a YAML string.

Every name that ends up on disk — an interface name, a request name, a folder name — comes from something you typed, so it goes through path-safety rules before it is written: characters illegal on any supported OS are stripped, and a name cannot escape the project folder.

File Holds
wirebench.yaml The project manifest: id, name, settings (timeouts, caching, pretty-printing), properties, which of them are disabled, and the active environment
environments/<slug>.yaml One environment’s endpoint overrides and properties
interfaces/<Interface>/interface.yaml A SOAP interface: its WSDL/XSD reference, endpoints, WS-Addressing policy and auth (a reference, never a credential)
apis/<Api>/api.yaml A REST API: its OpenAPI reference, base URL, settings and auth reference
<Request>.request.yaml + <Request>.xml (SOAP) or <Request>.body.json (REST) One saved request: metadata in YAML, the payload in a file of its own kind
<Request>.pre.ts, <Request>.post.ts A request’s pre-request and post-response scripts, named by its scripts key. They are TypeScript, or JavaScript (.pre.js, .post.js) for scripts imported from Postman. Renaming or moving the request moves them
<Request>.golden.yaml A request’s snapshot: the golden response body, its content type, when it was saved and the ignore rules. It sits beside the request’s own file, outside the project model, so an older build leaves it alone and it needs no format-version change. Renaming or moving the request leaves it behind. See Snapshot regression
sequences/<Sequence>.sequence.yaml One sequence: its steps in order, each naming a saved request by id, with the step’s transfers and assertions and the run settings. It carries kind: sequence and its own version (see below)
mocks/<Mock>/mock.yaml One mock service: the interface or API it implements (by id), for SOAP the binding it speaks, the port, the path and how it treats a request that breaks the contract (reject, report or off). It carries kind: mock and its own version (see below). The listening host is never in the file: it is chosen when the mock is started and defaults to loopback
mocks/<Mock>/operations/<Operation>/operation.yaml One operation of a mock: the contract operation it answers (the operation name for SOAP, <method> <path template> for REST), how it picks a response (sequence, random, match or script) and its default response
<Response>.response.yaml + <Response>.body.<ext> One canned response: status, headers, delay, the conditions under which it is picked and the scenario state it needs and sets. The body sits beside it in its own language and is sent exactly as written — no ${…} is expanded in it. A response with values echoes request values into its body and header values with {{name}} (version 2, see Echoing request values)
definition/ The fetched or imported API definition (WSDL, XSD, OpenAPI), kept byte-exact, plus a manifest mapping each URL to its cached file and checksum
wss/ WS-Security configuration and keystore entries — no secret values
attachments/ Files attached to a request, stored by content hash

A credential is never written into a project file. Auth fields carry a secretRef that points into the OS keychain instead; a project file that somehow contained a plaintext password, token or similar key is refused rather than saved.

Every kind of YAML file above has a published JSON Schema (draft 2020-12), generated from the schemas Wirebench itself reads the files with, so an editor flags a mistyped key or a wrong value before Wirebench does. They are served under https://wirebench.github.io/wirebench/docs/schemas/v<format>/, one folder per project format version, and attached to every GitHub release.

A schema describes what Wirebench accepts when it loads a file: a field with a default may be left out, and an unknown key is allowed, because it is ignored on load. A few checks are made only by Wirebench itself — a plaintext secret beside a secretRef, for one — so a file that passes its schema can still be refused when the project opens.

To validate a project in Visual Studio Code, install the Red Hat YAML extension and add this to the workspace’s .vscode/settings.json:

{
"yaml.schemas": {
"https://wirebench.github.io/wirebench/docs/schemas/v8/manifest.schema.json": [
"**/wirebench.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/environment.schema.json": [
"**/environments/*.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/interface.schema.json": [
"**/interfaces/*/interface.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/soap-request.schema.json": [
"**/interfaces/*/operations/*/*.request.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/interface-definition-manifest.schema.json": [
"**/interfaces/*/definition/manifest.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/api.schema.json": [
"**/apis/*/api.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/api-request.schema.json": [
"**/apis/*/requests/**/*.request.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/folder.schema.json": [
"**/apis/*/requests/**/folder.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/api-definition-manifest.schema.json": [
"**/apis/*/definition/manifest.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/golden.schema.json": [
"**/*.golden.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/sequence.schema.json": [
"**/sequences/*.sequence.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/mock.schema.json": [
"**/mocks/*/mock.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/mock-operation.schema.json": [
"**/mocks/*/operations/*/operation.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/mock-response.schema.json": [
"**/mocks/*/operations/*/*.response.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/webhooks.schema.json": [
"**/webhooks/webhooks.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/webhook-request.schema.json": [
"**/webhooks/requests/**/*.request.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/webhook-folder.schema.json": [
"**/webhooks/requests/**/folder.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/keystores.schema.json": [
"**/wss/keystores.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/wss-outgoing.schema.json": [
"**/wss/outgoing/*.yaml"
],
"https://wirebench.github.io/wirebench/docs/schemas/v8/wss-incoming.schema.json": [
"**/wss/incoming/*.yaml"
]
}
}

Pin the version folder to the project’s formatVersion: a project saved by a newer build moves to the next folder.

A workspace groups several projects under one set of environments. It normally lives entirely inside Wirebench’s app data folder (see Install and first run for the exact path per OS):

workspaces/<id>/
workspace.yaml # id, name, properties, project references
environments/<slug>.yaml # one file per workspace environment: properties + endpoint overrides
projects/<slug>/ # internal projects — ordinary project folders, unchanged format
local.yaml # machine-local state (active environment); never shared or synced
share.yaml # present only for a shared workspace: git remote/branch or a synced folder path

A project inside a workspace is byte-for-byte the same folder described above — the workspace only adds files around it. A project can also be linked: an external folder the workspace reads and writes in place instead of copying in, which is how a project meets git when you want version control. local.yaml and share.yaml are never part of what a shared workspace syncs to teammates.

Each project file’s formatVersion is a commitment: an additive field is a breaking change here, because an unknown key is dropped on load and would otherwise be silently lost the next time an older build saves the file. Any format change — added, changed or removed — bumps the version and ships a migration; opening a project from a newer version fails with a clear error instead of guessing:

Project was created by a newer version of Wirebench (format N, this build supports M)

The project format is currently version 8:

  1. Version 1 — the original layout described above, without APIs or per-variable disabling.
  2. Version 2 — every property scope gained a per-variable disabled list, so a variable can be switched off without deleting it. Omitted when empty; a version-1 file migrates as “all enabled”.
  3. Version 3 — projects can hold REST APIs beside interfaces, in the apis/ tree shown above. A version-1 or version-2 project opens unchanged and is rewritten at version 3 on its next save.
  4. Version 4 — a request can carry assertions, and each secret reference can name the environment variable a CI run reads it from (…Env).
  5. Version 5 — a SOAP interface, endpoint or request can use bearer, API-key and OAuth2 auth, previously REST-only.
  6. Version 6 — a SOAP, REST or gRPC request can carry a scripts key, with its pre-request and post-response scripts in files beside it, and a WebSocket request can carry assertions.
  7. Version 7 — a REST request can carry examples: saved responses, each with its body in a file beside the request, under <slug>.examples/<id>.body.<ext>.
  8. Version 8 — auth can be Kerberos (type: kerberos), on a request or wherever auth is inherited from; a version-7 project migrates unchanged.

A sequence file is versioned on its own (version: 1), not by formatVersion. An older build never reads, lists or deletes sequences/, so it opens the project and leaves the folder exactly as it was. A build that finds a sequence file newer than it understands, malformed, or sharing another’s id reports it as a problem, skips it, and never deletes or overwrites it on save.

Mock files follow the same rule: mock.yaml carries version: 1, or version: 2 when one of its responses has values (ADR-0022), and the operation and response files under it belong to that version. An older build leaves mocks/ exactly as it was. A build that finds a mock file it cannot load (mock-file-invalid, mock-version-too-new, mock-duplicate-id) skips that file — a bad mock.yaml skips its whole mock — and a save never deletes or overwrites it (mock-file-conflict). See ADR-0021.

A workspace manifest has its own, independent format version (WORKSPACE_FORMAT_VERSION, currently 3), versioned and migrated the same way, with the same “created by a newer version” error when a workspace’s format is ahead of what the build understands.

Migration only ever moves forward: a build cannot open a project or workspace written by a newer build, even if nothing it actually uses has changed.