Features

Simplicity is tied in.
All in one workbench — no tool switch required.

Each section below links to the technical documentation for the full details.

Discover and invoke

Plug a protocol in, get a live service tree. Compose requests with typed forms or raw JSON. Every call pattern — unary, streaming, duplex, REST verbs, GraphQL, MQTT publishes — ends up in the same UI.

Auto-discovery with hot reload

Plug in a protocol, get a service tree — no hand-maintained proto / OpenAPI / hub-registration code.

Each protocol plugin implements a single DiscoverAsync method: gRPC hits Server Reflection, REST reads the OpenAPI spec, SignalR enumerates mapped hubs via EndpointDataSource, GraphQL introspects __schema, MQTT subscribes to $SYS/#, and so on. Bowire merges the results into a single sidebar across every installed plugin.

For event-driven APIs that come with their own contract, point Bowire at an asyncapi.yaml / .json document (2.x + 3.0). The AsyncAPI plugin parses channels + operations + bindings, then routes invocations through whichever wire plugin the doc declares — today http (no broker needed), mqtt / mqtt5, kafka, ws, and amqp / amqp1. NATS lands alongside the matching wire plugin.

The discovery pass repeats every few seconds against every configured URL. A new method registered on a running server shows up in the sidebar within the next tick — no page refresh, no re-upload, no “disconnect and retry” dance.

Read the technical docs →
Discovered service method with typed parameters, exposed in the sidebar without hand-maintained schema Discovered service method with typed parameters, exposed in the sidebar without hand-maintained schema

AsyncAPI dispatches through every shipped wire

One asyncapi.yaml, five wire plugins. Each bindings: key the doc declares has a resolver that hands the call to the matching shipped plugin.

The AsyncAPI plugin doesn’t talk to brokers itself — it’s a discovery source that reads the contract and dispatches. Today every active binding maps to a real, shipped plugin: bindings.mqtt / mqtt5 to Kuestenlogik.Bowire.Protocol.Mqtt, bindings.kafka to Kuestenlogik.Bowire.Protocol.Kafka, bindings.amqp / amqp1 to Kuestenlogik.Bowire.Protocol.Amqp, bindings.ws to Kuestenlogik.Bowire.Protocol.WebSocket, and bindings.http through Bowire’s built-in HTTP client.

The benefit: an AsyncAPI doc travels as the contract surface (review it in PRs, version it in Git), but the runtime is whatever wire the broker speaks. Switch from Kafka to MQTT by editing the bindings: block; no Bowire side changes needed.

Read the AsyncAPI docs →
flowchart LR
    Doc[("asyncapi.yaml")]
    AAPI(["AsyncAPI plugin"])
    Res{"BindingResolver"}
    MQTT(["Protocol.Mqtt"])
    Kafka(["Protocol.Kafka"])
    AMQP(["Protocol.Amqp"])
    WS(["Protocol.WebSocket"])
    HTTP(["HTTP (built-in)"])
    NATS(["Protocol.Nats — follows"])

    Doc --> AAPI
    AAPI -->|"channels · operations · bindings"| Res
    Res -->|"bindings.mqtt / mqtt5"| MQTT
    Res -->|"bindings.kafka"| Kafka
    Res -->|"bindings.amqp / amqp1"| AMQP
    Res -->|"bindings.ws"| WS
    Res -->|"bindings.http"| HTTP
    Res -.->|"binding pending"| NATS
        
Five resolvers shipped today, one per binding family. The AsyncAPI plugin reads the contract, the resolver routes the call — the wire plugin does the actual broker work.

Every call pattern in one UI

Unary, server-stream, client-stream, duplex — plus REST verbs, GraphQL subscriptions, SignalR hubs, raw WebSocket frames, MQTT publishes. One UI, same muscle memory across all of them.

Streaming calls render into a Wireshark-style append-only list with per-frame detail. Duplex / client-stream open a persistent channel with a “send next message” composer on one side and incoming frames on the other. REST maps verbs onto the same invoke pane; GraphQL has a selection-set picker plus subscription follow-up via graphql-transport-ws.

Read the technical docs →
Streaming response — Wireshark-style frame list with detail pane Streaming response — Wireshark-style frame list with detail pane

Form or raw JSON

Schema-driven form inputs for the common case, a full JSON editor when you need to tweak something unusual. Toggle with f.

The form view auto-generates controls from the discovered message shape: typed number inputs for int32, date pickers for google.protobuf.Timestamp, dropdowns for enums, nested cards for messages, repeated-field tables. Client-side validation flags missing required fields and type mismatches before a single byte hits the wire.

The JSON view is a full editor with syntax highlighting, bracket matching, and inline validation errors. State is preserved per method, so switching away and back never loses work.

Read the technical docs →
json press f for form view
{
  // typed form mirrors this shape
  "city": "Berlin",
  "days": 7,
  "units": "celsius",
  "include_humidity": true,
  "filter": {
    "after": "2026-04-22T00:00Z"
  }
}

AI agents drive the workbench via MCP

Pass --enable-mcp-adapter and every method Bowire discovered becomes an MCP tool an AI agent can call directly. JSON Schema input, typed responses, no MCP-server boilerplate to write.

Claude Desktop, Cursor, Copilot, custom MCP clients — same wire, same auth, same recordings as the human-driven workbench. The adapter generates tool definitions automatically from the discovered service tree, so a new method on a running server is callable by the agent within the next discovery tick. Streaming responses surface as MCP streams; auth flows through whichever provider the workbench itself runs under.

Same idea as bowire mcp serve below, but flipped: that one exposes Bowire's own primitives as MCP tools. This one exposes the target API's methods. Run both side by side if you want an agent that can both drive your service surface and orchestrate Bowire's record / mock / replay primitives.

Read the technical docs →
bash $ bowire ... --enable-mcp-adapter
$ bowire --url https://api.example.com \
          --enable-mcp-adapter

   # workbench    http://localhost:5080
   # MCP adapter  http://localhost:5080/mcp
   # 12 discovered methods exposed as MCP tools

Bowire's own ops as MCP tools

bowire mcp serve exposes Bowire itself as an MCP server. The agent can chain discover → invoke → assert → switch env → replay the same way a human would in the workbench, just with more parallelism.

Primitives exposed: bowire.discover, bowire.invoke, bowire.subscribe, bowire.env.list, bowire.record.{list, start, stop, replay}, bowire.mock.{start, stop, list}, bowire.allowlist.show. Two transport modes: stdio for desktop clients (Claude Desktop, Cursor) and HTTP for remote agents (Copilot in CI, custom MCP runners).

Auth piggy-backs on whichever provider the workbench runs under, so the agent never sees a second credentials store. Tools are gated by the same allow-list the workbench uses — an agent can't reach a method a human couldn't.

Read the technical docs →
bash $ bowire mcp serve
# Stdio — what Claude Desktop / Cursor want
$ bowire mcp serve --bind stdio

# HTTP for over-the-wire clients (Copilot, CI runners)
$ bowire mcp serve --bind http --port 5097

Build flows and chain responses

Compose multi-step API sequences visually, forward any response value into the next call, and drive everything from the keyboard.

Visual flow editor

Chain API calls into branching pipelines — visually, not in a YAML file you can’t read three months later.

Drag requests onto a canvas, forward response values into the next call with ${response.path.to.value}, branch on status or body content, loop over arrays, sleep, assert. Every node has its own response viewer so you can step through a failing flow like a debugger.

All of it is local to your machine. No seat-counted cloud subscription, no hand-maintained Postman Collections — flows are JSON files you can commit, review, and replay.

Read the technical docs →
Visual flow editor — pipeline of API calls with branching conditions and loops Visual flow editor — pipeline of API calls with branching conditions and loops

Click any JSON value to chain

The response from one call is the input for the next. Bowire skips the copy-paste round-trip.

Hover any value or key in a rendered JSON response, click it, and Bowire places the chaining expression on your clipboard — ready to paste into the next request’s body, URL path, or headers. Works on every supported protocol, not just REST: gRPC response fields, GraphQL query results, SignalR hub return values, MQTT payloads, SSE events all use the same ${response.path.to.value} grammar.

Read the technical docs →
Click any JSON value in a response to copy the chaining variable expression Click any JSON value in a response to copy the chaining variable expression

Command palette & keyboard flow

Press / anywhere to search methods, switch environments, filter protocols — never leave the keyboard.

Vim-style j / k to hop between methods, f to flip form / JSON, r to repeat the last request, t to cycle themes, Ctrl+Enter to execute. Recently-used methods surface when the palette query is empty. Power users who live in the shell will feel at home.

Read the technical docs →
Command palette open with method suggestions, environment jumps, and protocol filters Command palette open with method suggestions, environment jumps, and protocol filters

Keyboard-first everything

Every action has a shortcut. Power users never touch the mouse; newcomers get a cheat sheet at ?.

Vim-style j / k to hop between methods in the sidebar, f to flip form ↔ JSON, r to repeat the last request, t to cycle light / dark / auto themes, Ctrl+Enter (or +Enter) to execute, Esc to close any modal, / to open the command palette, ? to see the full list without leaving the page.

Shortcuts are configurable per-user in Settings → Shortcuts, with conflict detection so the same chord never binds two actions silently.

Read the technical docs →
shortcuts press ? for the full list
/             open command palette
j  / k        hop between methods
f             toggle form ↔ JSON
r             repeat last request
⌘ Enter       send request
t             cycle theme
Esc           close modal
?             full cheat sheet

Capture, replay, and stress

Every call captured in a filterable timeline, replayable against any environment, and ready for a quick load check — all in the same workbench.

Record & replay

Click record, run a sequence of calls, click stop. Now you have a reproducible test case, a debug artifact, and a future mock server in one file.

Recordings capture request, response, status, timing, and metadata for every call across every protocol — gRPC streams, SignalR hub invocations, WebSocket frames, MQTT publishes, REST hits, all in one timeline. Replay against another environment to check drift, convert into test assertions for CI, export as HAR for teammates, or serve the recording back as a live mock (see the next block).

Read the technical docs →
Recording manager — timeline of captured calls with per-step pass/fail markers Recording manager — timeline of captured calls with per-step pass/fail markers

Replay recordings as a live mock server

bowire mock --recording session.bwr --port 5099 turns any captured recording into a live endpoint at the same shape as the original — so dev / test runs don't have to stand up the real backend.

The real backend is friction. The third-party API needs an OAuth flow + a paid subscription that burns credits on every test run. Your own service is a 12-container docker-compose with a 5-minute boot. The CI runner sits behind a corporate VPN that doesn't reach the upstream at all. A captured recording cuts all of that out of the loop — the fixture is the JSON file, drop it on a USB stick, drop it in a Git repo, hit replay.

Every protocol your recording captured replays through the same listener — REST verbs on the same paths, gRPC unary + server-streaming + bidi with the captured frames, WebSocket / SSE / SignalR / Socket.IO pushing the same sequence, MQTT publishes hitting the same topics, GraphQL subscriptions streaming the same events. Protocol semantics stay intact (status codes, frame timing, headers) instead of collapsing everything to a JSON stub.

Optional layers on top: hot-reload — rewriting the recording file refreshes the mock without restart; chaos injection — configurable latency / 5xx rate / dropped-frame ratio for resilience tests; schema-only mode (--from-spec openapi.yaml) — plausible responses from an OpenAPI / gRPC descriptor / GraphQL schema with no recording at all; capture-on-miss — unrecorded request transparently forwards to a fallback target, captures the response, serves it back next time.

Develop without the real backend — full writeup → Read the technical docs →
bash $ bowire mock
$ bowire mock \
    --recording session.bwr \
    --port 5099 --hot-reload

   # mock listening on 0.0.0.0:5099
   # 42 calls loaded across gRPC, REST, WebSocket

Multi-protocol security scanner

bowire scan --target https://your-api --out findings.sarif runs vulnerability templates against the API surface Bowire already discovered, and emits SARIF 2.1.0 that your CI ingests unchanged.

Two template sources at once: Bowire's own JSON templates (curated baseline shipped in Bowire.VulnDb) plus the 8000+ projectdiscovery/nuclei-templates via --nuclei <dir>. Both feed Bowire's native AttackPredicate engine — same matchers, same payload variables, no separate runtime.

Findings emit as SARIF 2.1.0 — straight into GitHub Code Scanning, GitLab Security Dashboard, Azure DevOps. Built-in passive checks (TLS-version enumeration, version-disclosing headers, verbose-error detection) fire on day one without writing a template. Gating happens at the SARIF-consumer level (Code Scanning branch-protection rules) so the scan step itself stays linear.

See the security solution overview →
bash $ bowire scan
$ bowire scan --target https://api.example.com \
              --nuclei ~/nuclei-templates \
              --out findings.sarif --severity medium

  142 template(s) processed:
  [ok]   built-in.tls-1.0          medium
  [VULN] built-in.banner-disclose  low      Server: nginx/1.18.0
  [VULN] CVE-2024-12345            high     /api/v1/admin (header bypass)

  SARIF report -> findings.sarif

Schema-aware field-level fuzzer

bowire fuzz mutates one field at a time using the schema Bowire already knows. Knows not to throw SQL-injection at a latitude (lat) field; image.bytes gets magic-byte mutation, not XSS strings.

Schema-awareness is the differentiator: the fuzzer respects field types from the discovered schema (OpenAPI / proto / GraphQL / AsyncAPI). Image-byte fields get magic-byte / truncation / over-length mutation, integer fields get boundary / overflow / negative mutation, dates get format / range mutation, strings get the category-appropriate payloads.

Four payload categories ship out of the box: sqli, xss, pathtrav, cmdinj with curated wordlists. A baseline-diff oracle flags responses that look materially different from the baseline — status change, error-shape divergence, latency spike beyond N standard deviations. Findings reuse the same SARIF emitter as scan, so they drop into the same Code-Scanning surface.

Read the technical docs →
bash $ bowire fuzz
$ bowire fuzz \
    --target https://api.example.com \
    --template recordings/order.bwf \
    --field '$.customer.email' \
    --payloads sqli,xss --out fuzz.sarif

  payloads tried:  42
  baseline runs:    5
  divergent:        3   (status 500, latency >3σ)

Wireshark-style streaming view

An append-only message list on the left, a detail pane on the right — built for long-running duplex sessions, not a chat transcript.

Every incoming frame lands in a filterable table (substring, regex, or protocol-specific fields). Click one to see the full payload. Pin the latest to auto-follow, or pin a specific frame to keep the detail view stable while new messages scroll in. Works for gRPC server / client / duplex streams, SignalR real-time events, GraphQL subscriptions, WebSocket frames, SSE events, and MQTT subscribe streams — all with the same UI.

Read the technical docs →
Streaming response view — append-only message list on the left, detail pane on the right Streaming response view — append-only message list on the left, detail pane on the right

Performance testing, built in

Repeat any call N times, watch the latency histogram and timeline render in real time.

P50 / P90 / P99 / P99.9 percentiles, success rate, and throughput all in the same pane as the request. No separate load tool to install, no CI pipeline to wire up — just a quick “is this endpoint holding up?” check when you suspect a regression. Scales enough for smoke tests; for full-blown soak tests, export the call as k6 / vegeta and run it from a proper load runner.

Read the technical docs →
Performance graph — latency histogram with P50 / P90 / P99 percentiles Performance graph — latency histogram with P50 / P90 / P99 percentiles

Inline test assertions

Write Newman-style checks once, Bowire runs them after every successful response and shows a green / red badge inline.

Eleven comparison operators (eq, ne, gt, gte, lt, lte, contains, startsWith, endsWith, matches, type) target any value via response.path.to.value JSONPath. Assertions are stored per method, replay with every invocation, and can be exported into a recording so a flow becomes its own regression test.

For streaming responses, assertions run on the accumulated frames — “expect at least 3 frames of type TICK within 2 s” is one rule. No plugins, no pipeline, just a test that lives next to the request.

Read the technical docs →
assertions 4/4 passing
response.status   eq   200        
response.time     lt   500        
body.user.id      eq   "42"       
body.items[0]     type "object"   

Environments, schemas, and setup

Compare what’s actually running where, work from a schema when the server isn’t reachable, authenticate against any auth scheme, and script everything from the CLI when you don’t want the UI.

Globals and Environments

A typed variable store per environment, plus a Globals layer everyone inherits — the same ${name} you'd reach for in any HTTP client, scoped properly.

Define Globals once (machine-wide constants you never change — timeouts, retry-counts, the base URL of your local Docker registry) and per-environment named scopes on top of them (Dev, Staging, Production, …). Variables surface as ${api_url}, ${timeout_ms}, ${prod_token} in any request body, metadata header, URL field, script — the workbench substitutes at request time with the resolution chain Globals → active environment, per-environment overrides winning by name.

Variables persist to ~/.bowire/environments.json on disk (the workbench mirrors them to localStorage for instant access). Export / Import buttons round-trip the full env set as a .bwe file — commit it to your repo, share it with a colleague, restore it on a fresh laptop. Switching the active environment from the topbar instantly re-resolves every ${...} in the request editor so a single keystroke flips between dev and prod without a single textual edit.

Read the technical docs →
Environments editor — Variables tab showing a populated Staging environment alongside Globals and Production in the sidebar Environments editor — Variables tab showing a populated Staging environment alongside Globals and Production in the sidebar

Environment diff

Compare two environments side-by-side — spot drift before it hits production.

Pick any two configured environments (Dev, Staging, Prod, or per-URL overrides) and get a colour-coded diff across every variable and header. Equal rows are folded away, changed rows highlighted, only-in-A / only-in-B clearly marked. Catches missing secrets, mistyped keys, and accidental overrides that would otherwise burn an afternoon of debugging.

Read the technical docs →
Environment diff view — two environments compared side by side with colour-coded rows Environment diff view — two environments compared side by side with colour-coded rows

Every auth scheme, built in

Every auth scheme worth supporting, built in. Set it once per environment, Bowire signs every request.

Bearer tokens, HTTP Basic, API Key (header or query), JWT issuance (HS256 / RS256 / ES256) with custom claims, OAuth 2.0 client_credentials, OAuth 2.0 authorization_code with PKCE for local CLI flow, a custom token endpoint with auto-refresh, and AWS Signature v4 for S3 / Lambda / API Gateway. Each scheme stores its values in the environment, substituted at request time — tokens never touch the page source.

Expired tokens refresh transparently. Metadata headers on gRPC, authorization headers on REST / GraphQL / SignalR / WebSocket / MQTT (Username-Password CONNECT) all share the same credential store.

Read the technical docs →
Auth tab in the env editor — OAuth 2.0 client_credentials with token URL, client ID, scope, audience, plus a Fetch-token test button Auth tab in the env editor — OAuth 2.0 client_credentials with token URL, client ID, scope, audience, plus a Fetch-token test button

Work offline from a schema file

Drop in a .proto, OpenAPI spec, GraphQL SDL, or AsyncAPI document — browse the API surface without a running server.

Great for reviewing a contract during a PR, sharing a service’s shape with a teammate who can’t reach staging, prototyping a client before the backend exists, or teaching someone what an API looks like without spinning up infrastructure. Supports .proto (buf-style imports included), OpenAPI 3 / Swagger 2, GraphQL SDL, and AsyncAPI 2.x / 3.0 — other formats via plugin.

Read the setup docs →
Schema upload — drop in a .proto, OpenAPI spec, or GraphQL SDL and browse the API offline Schema upload — drop in a .proto, OpenAPI spec, or GraphQL SDL and browse the API offline

Smart empty states

Every screen where Bowire could be blank has something useful instead — a welcome, a retry, a diff of what connected and what didn’t.

First-run without a --url gets two onboarding paths: connect to a live server, or drop in a schema file for offline browsing. When discovery fails, the page surfaces the actual error plus four common-cause troubleshoot bullets. With multiple URLs configured, a per-URL status table lets you retry the ones that failed without touching the ones that connected.

When everything works, the landing shows a service summary, recent-history quick-recall, keyboard shortcut tips, and a guided tour entry point for new team members.

Read the technical docs →
Connected & ready landing — service summary, recent history, keyboard tips Connected & ready landing — service summary, recent history, keyboard tips First-run welcome screen with two onboarding cards First-run welcome screen with two onboarding cards Multi-URL status table with retry buttons for failed servers Multi-URL status table with retry buttons for failed servers Discovery-failed state with error and troubleshooting bullets Discovery-failed state with error and troubleshooting bullets

No CORS proxy to set up

Bowire talks to any API — local, internal, cloud — without browser-CORS friction. The architecture is the proxy.

The UI only ever hits localhost:5080 (same-origin, no CORS involved). The actual gRPC, REST, GraphQL, WebSocket, SSE, MCP, and MQTT calls are made by the Bowire process server-to-server, not from the browser. Same-origin policy doesn’t apply to server-to-server traffic, so endpoints without permissive CORS headers still work — no proxy layer to configure, no dev-tunnel to spin up, no *-origin security hole to poke.

It’s the reason you can browse a staging gRPC cluster or an internal REST service from your laptop without the backend team adding Access-Control-Allow-Origin.

Read the architecture docs →
flowchart TD
    Browser(["Browser"])
    Host(["Bowire host"])
    API(["Target API"])
    Browser -->|"localhost:5080 (same-origin, no CORS)"| Host
    Host -->|"upstream (server-to-server, no SOP)"| API
        
The browser only ever hits localhost:5080 — same-origin, so CORS never enters the picture. Bowire makes the upstream call (gRPC, REST, WebSocket, MQTT, …) server-to-server, where same-origin policy doesn’t apply.

Install, update, inspect — protocol plugins as NuGet packages

Every protocol plugin is a NuGet package. bowire plugin install fetches it, update bumps it, inspect shows what's loaded. No dotnet restore detour, no .NET SDK required at install time — only the runtime.

Plugins install into ~/.bowire/plugins/ (override via --plugin-dir or appsettings.json), each in its own isolated AssemblyLoadContext so two plugins can ship different versions of the same third-party library without clashing. Shared contract assemblies (Kuestenlogik.Bowire.*, System.*, Microsoft.*) delegate to the host so IBowireProtocol stays type-identity-identical across the boundary.

Need a private feed? --source https://nuget.mycorp.internal/v3/index.json is repeatable; multiple sources are tried in order. bowire plugin inspect MyCompany.Protocol.X loads the plugin live and reports the load-context, the resolved version, every assembly it pulled in, and every IBowireProtocol implementation reflection finds — instant verification that a freshly-packed plugin is wired correctly.

Read the technical docs →
bash $ bowire plugin ...
$ bowire plugin install Acme.Bowire.Protocol.Amqp
  Installed Acme.Bowire.Protocol.Amqp 1.2.0 (4 file(s))

$ bowire plugin update                     # bump all
  Updating Acme.Bowire.Protocol.Amqp 1.2.0 -> 1.3.0...

$ bowire plugin inspect Acme.Bowire.Protocol.Amqp
  Load context
    name:        BowirePlugin:Acme.Bowire.Protocol.Amqp
    collectible: True
    assemblies:  4
      Acme.Bowire.Protocol.Amqp 1.3.0
      RabbitMQ.Client            7.0.0
      ...
  Bowire contract implementations
    IBowireProtocol  Acme.Bowire.Protocol.Amqp.AmqpProtocol

Per-plugin settings

Every protocol plugin contributes its own settings section — granular control without leaving the UI.

General settings (theme, keyboard shortcuts), data management (where recordings, history, and environments live on disk), and a dedicated panel per installed protocol plugin. Each plugin implements its own BowirePluginSetting schema, so the UI always reflects exactly what that plugin supports — no hidden configuration in environment variables or JSON files.

Read the technical docs →
Settings dialog — general, shortcuts, data management, and per-plugin configuration Settings dialog — general, shortcuts, data management, and per-plugin configuration

One binary, three modes

Run as a .NET global tool and get three flavours of the same workbench — CLI subcommands for scripts, an HTTP-served UI for interactive use, and embedded middleware for teams that want the UI inside their own app.

bowire list <url> enumerates services, bowire describe <service.method> prints the full schema, bowire call <service.method> --body '{...}' invokes it — same output shape as the UI uses, so the CLI is scriptable against CI. Add --format json for machine-readable piping, or --recording to capture an output for later replay.

The standalone UI is just bowire --url https://api.example.com — the UI runs locally, hits the API with your auth, zero host-side changes. Plugin management (bowire plugin install / list / uninstall) shares the same state with the embedded mode.

Read the technical docs →
bash $ bowire
$ bowire list --url https://api.example.com
WeatherService   gRPC
UserApi          REST
EventsHub        SignalR

$ bowire call UserApi.GetUser -d '{"id":42}'
{ "id": 42, "name": "Ada" }

$ bowire --url https://api.example.com  # opens UI

Intercepting proxy with HTTPS MITM

bowire proxy is a CA-trusted forward proxy that decrypts TLS on the fly, streams captured flows into the workbench, and converts any flow into a Bowire recording with one click.

Auto-generated CA on first run, leaf-cert minting per hostname, cache that survives restarts. Point your client (browser, mobile app under test, scripted runner) at localhost:8888, install the printed CA, every request flows through the proxy — HTTPS included, fully decrypted. The workbench Proxy view subscribes to the flow stream over SSE and renders each captured request/response live.

One-click "send to recording" converts a captured flow into a template that bowire scan, fuzz, or mock can replay. The CA lives at ~/.bowire/proxy-ca.{pfx,crt} — same place across restarts, importable into the trust store of any client you're driving.

Read the technical docs →
bash $ bowire proxy
$ bowire proxy --port 8888 --api-port 8889

   # proxy        listening on 0.0.0.0:8888
   # workbench    http://localhost:8889/proxy
   # CA exported  ~/.bowire/proxy-ca.crt

$ bowire proxy --export-ca ~/Downloads/bowire-ca.crt

JWT decode, tamper, re-sign

bowire jwt decode splits the header / payload / signature, validates exp / nbf / iat, lists the present claims. bowire jwt tamper probes the classic alg: none acceptance, signs with a chosen secret, lets you override individual claims for the smoke-test sequence.

Scriptable enough for CI gates and security-regression suites — same exit codes / SARIF output as the rest of the security CLI surface so a failed alg:none downgrade attempt or a successful claim-injection lands in the Code-Scanning dashboard next to a regular scan finding.

Pairs naturally with the workbench's auth helpers above: capture a JWT from a real session via bowire proxy, run bowire jwt tamper against it to probe the verification surface, then re-inject the tampered token into the next request with the auth-helper's override flag.

Read the technical docs →
bash $ bowire jwt ...
# Decode header + payload, flag well-known weaknesses
$ bowire jwt decode <token>

# alg:none downgrade (CVE-2015-9235 class)
$ bowire jwt tamper <token> --alg-none

# Inject a claim, re-sign with a weak secret
$ bowire jwt tamper <token> \
                   --set isAdmin=true \
                   --secret weak123

Into the engine room

That was the topside tour. For SDK generation, protocol-plugin authoring, and everything else that lives below the waterline — read the docs or dive into the source.

The engine room runs on hands. Join the crew →