# Postman Client Diagnostics

CLI tool that diagnoses common issues with the Postman desktop client. Runs checks across multiple diagnostic suites:

- **Network** — DNS resolution, TCP connectivity (port 443), HTTPS reachability, and WebSocket upgrade handshakes to all required Postman domains
- **Terminal** — Local shell detection, process spawning, and PTY (pseudo-terminal) allocation health

Built for IT administrators, support engineers, and enterprise customers who need to diagnose connectivity and local environment issues.

## Download

Grab the latest artifact for your platform from [GitHub Releases](../../releases):

| Platform | Artifact |
|----------|----------|
| macOS (Apple Silicon) | `postman-client-diagnostics-darwin-arm64.zip` (contains `.app`) |
| Linux (x64) | `postman-client-diagnostics-linux-x64` |
| Windows (x64) | `postman-client-diagnostics-win32-x64.exe` |

These are standalone executables — no Node.js installation required. Intel Mac users can run via `npx` (see below).

> **Signing status:** Binaries are not yet code-signed. Before distributing to external users, the release pipeline needs proper signing:
>
> - **macOS** — Apple Developer ID certificate + notarization via `notarytool`
> - **Windows** — Authenticode code-signing certificate
> - **Linux** — GPG-sign the binary and publish the `.sig` alongside it
>
> Internal users can follow the per-platform steps below to run the unsigned builds.

### Running on macOS

1. Download `postman-client-diagnostics-darwin-arm64.zip` from [GitHub Releases](../../releases)
2. Double-click the `.zip` to extract (Safari does this automatically)
3. (Workaround since binary is not signed) Using your Terminal, run the below commands
5. A Terminal window opens and runs the diagnostics automatically

```bash
cd ~/Downloads
xattr -dr com.apple.quarantine "Postman Client Diagnostics.app"
open "Postman Client Diagnostics.app"
```

### Running on Windows

1. Download `postman-client-diagnostics-win32-x64.exe` from [GitHub Releases](../../releases)
2. Windows SmartScreen may show "Windows protected your PC" — click **More info**, then **Run anyway**
3. Alternatively, run from PowerShell or Command Prompt:

```powershell
.\postman-client-diagnostics-win32-x64.exe
```

If your organization uses AppLocker or WDAC policies that block unsigned executables, use the `npx` method instead.

### Running on Linux

1. Download `postman-client-diagnostics-linux-x64` from [GitHub Releases](../../releases)
2. Make it executable and run:

```bash
chmod +x postman-client-diagnostics-linux-x64
./postman-client-diagnostics-linux-x64
```

### Alternative: npm

If you already have Node.js 18+, no binary download is needed:

```bash
npx @postman/client-diagnostics
```

This works on all platforms including Intel Macs and avoids code-signing issues entirely.

## Usage

```
postman-client-diagnostics [options]

Options:
  --json            Output results as JSON (for scripting or support tickets)
  --suite <names>   Comma-separated list of suites to run (default: all)
                    Available: network, terminal
  --group <name>    Only test a specific group (network suite only)
                    Aliases: global, vscode, us-gateway, us-services,
                    eu-gateway, eu-services
  --region <name>   Region to test: us (default), eu, or all (network suite only)
                    Shared/global domains are always included
  --timeout <ms>    Override per-check timeout (default: 3000-5000ms)
  --no-color        Disable colored output
  -h, --help        Show help
```

### Examples

```bash
# Run all diagnostic suites
postman-client-diagnostics

# Run only network checks
postman-client-diagnostics --suite network

# Run only terminal/PTY checks
postman-client-diagnostics --suite terminal

# Run specific suites with JSON output
postman-client-diagnostics --suite network,terminal --json

# Test only the US sync/gateway domains
postman-client-diagnostics --suite network --group us-gateway

# Test the EU data center instead of the default (US) instance
postman-client-diagnostics --suite network --region eu

# Test both US and EU regions at once
postman-client-diagnostics --suite network --region all

# Pipe JSON output to a file for a support ticket
postman-client-diagnostics --json > diagnostics.json
```

### Exit codes

| Code | Meaning |
|------|---------|
| `0` | All critical checks passed (warnings are informational and don't affect exit code) |
| `1` | One or more critical checks failed |
| `2` | Only non-critical checks failed |

## Diagnostic suites

### Network suite

Tests connectivity to the full set of Postman-owned production service hosts, derived from the app's own configuration so a failed check pinpoints the exact feature affected. The default (`--region us`) scan covers 87 domains across the shared and US groups. `--region eu` swaps the US-specific domains for their EU data center equivalents (59 domains, since the shared domains are always included), and `--region all` tests both regions (110 domains). Checks run in order per domain: DNS → TCP → HTTPS → WSS. If an earlier check fails, later checks are skipped.

| Check | What it tests | How |
|-------|--------------|-----|
| **DNS** | Hostname resolves to an IP address | `dns.resolve4()` — fails if DNS is blocked or the domain doesn't exist |
| **TCP** | Port 443 is reachable | `net.connect()` on port 443 — fails if a firewall drops or rejects the connection |
| **HTTPS** | TLS handshake succeeds and server responds | `https.request()` HEAD — reports the actual HTTP status code (200, 301, 403, etc.) |
| **WSS** | Network path allows WebSocket traffic | Attempts `wss://` upgrade via the `ws` library. **Pass** if the connection opens OR the server responds with an HTTP status code (e.g. 502). **Warn** on timeout when HTTPS already passed. **Fail** only on connection reset or when HTTPS also failed. |

#### Interpreting WSS results

Postman's bifrost servers reject WebSocket upgrade requests from unauthenticated clients with HTTP 502. This is expected — the tool is not a real Postman client. What matters is whether the network allows the request to reach the server at all.

| WSS result | What it means |
|-----------|---------------|
| `wss 52ms` (no HTTP status) | Full WebSocket connection opened. Rare with anonymous clients. |
| `wss 1484ms 502` | Server responded HTTP 502 to the upgrade. Network path is open. The 502 is the server rejecting an anonymous client, not a network issue. |
| `wss WARN` (timeout) | No response to the upgrade request, but HTTPS already passed for this host. The network path is proven open. The server may simply not respond to anonymous WebSocket upgrades (common on several bifrost `.co` hosts). Does not affect the exit code. |
| `wss FAIL` (connection reset) | Connection was forcibly closed during the upgrade, often by a firewall or proxy sending TCP RST. |

#### Domains tested

26 domains across 5 groups, covering all versions of Postman:

- **Authentication & Core Services** — `challenges.cloudflare.com`, `identity.getpostman.com`, `api.getpostman.com`, `web.postman.co`, `desktop.postman.com`, `go.pstmn.io`
- **VS Code Extension** — `bifrost-extension-https-global.gw.postman.com`, `bifrost-extension-global.gw.postman.com`
- **Web App v10+ (WebSocket)** — 9 `bifrost-web-*` domains
- **Desktop App v10+ (WebSocket)** — 5 `bifrost-*-global` domains
- **Desktop App v10+ (HTTP)** — 4 `bifrost-*-https-*` domains

Full list based on [Postman's firewall documentation](https://learning.postman.com/docs/getting-started/installation/installation-and-updates/#use-postman-behind-a-firewall).

### Terminal suite

Tests whether the local environment can run a shell and allocate a pseudo-terminal (PTY). This suite diagnoses the "initializing terminal..." freeze in the Postman desktop app, which is typically caused by EDR (Endpoint Detection & Response) software or IT policies blocking PTY allocation.

| Check | What it tests | How |
|-------|--------------|-----|
| **Shell detection** | Default shell binary exists and is executable | Reads `$SHELL` (Unix) or `%COMSPEC%` (Windows), verifies the binary with `fs.access` |
| **Shell spawn** | A child process can be spawned from the shell | `child_process.execFile(shell, ['-c', 'echo ok'])` with timeout — fails if EDR blocks basic process spawning |
| **PTY spawn** | A pseudo-terminal can be allocated | Uses a layered strategy (see below) — fails if EDR blocks PTY allocation specifically |

#### PTY spawn strategy

The PTY check uses three strategies in priority order:

1. **`node-pty`** (dynamic import) — If `node-pty` is installed in the environment, the check uses it to spawn a PTY with the same parameters Postman Terminal uses (`xterm-color`, 80x30, home cwd, full env). This is the highest-fidelity check because it exercises the exact same native code path. `node-pty` is **not** bundled with this tool to avoid false positives from native module installation issues (see [ADR-001](adrs/001-suite-architecture-and-terminal-diagnostics.md)).

2. **`script` command** (macOS/Linux) — Falls back to the OS `script` utility, which allocates a real kernel PTY without requiring any native modules. This covers most enterprise environments.

3. **Skip** (Windows without `node-pty`) — On Windows there is no OS-level PTY equivalent. The check reports `skip` with a message explaining how to install `node-pty` for a full PTY test.

#### Interpreting terminal results

| Result pattern | Likely cause |
|---------------|-------------|
| All three pass | Terminal environment is healthy |
| Shell detection fails | `$SHELL` / `%COMSPEC%` not set or binary missing |
| Shell spawn fails, detection passed | EDR blocking basic process spawning from this binary |
| PTY spawn fails, shell spawn passed | **EDR blocking PTY allocation specifically** — this is the most common cause of the "initializing terminal..." freeze |
| PTY spawn skipped | `node-pty` not installed and no OS-level fallback (Windows). For a definitive PTY test, install `node-pty`: `npm install node-pty` |

## JSON output

```bash
postman-client-diagnostics --json > report.json
```

Results are organized by suite:

```json
{
  "timestamp": "2026-05-11T12:00:00.000Z",
  "node": "v20.11.1",
  "platform": "darwin arm64",
  "suites": {
    "network": {
      "results": [
        {
          "name": "Cloudflare Challenges (Sign-in)",
          "group": "Authentication & Core Services",
          "status": "pass",
          "critical": true,
          "ms": 94,
          "detail": {
            "hostname": "challenges.cloudflare.com",
            "proto": "https",
            "checks": {
              "dns": { "status": "pass", "ms": 16, "ips": ["104.18.95.41"] },
              "tcp": { "status": "pass", "ms": 19 },
              "https": { "status": "pass", "ms": 59, "statusCode": 405 },
              "wss": { "status": "skip" }
            }
          }
        }
      ],
      "summary": { "total": 26, "passed": 26, "warned": 0, "failed": 0, "criticalFailed": 0 }
    },
    "terminal": {
      "results": [
        { "name": "Shell detection", "group": "Terminal", "status": "pass", "critical": true, "ms": 0, "detail": { "shell": "/bin/zsh" } },
        { "name": "Shell spawn", "group": "Terminal", "status": "pass", "critical": true, "ms": 6 },
        { "name": "PTY spawn", "group": "Terminal", "status": "pass", "critical": true, "ms": 12, "detail": { "method": "script", "shell": "/bin/zsh" } }
      ],
      "summary": { "total": 3, "passed": 3, "warned": 0, "failed": 0, "criticalFailed": 0 }
    }
  },
  "summary": { "total": 29, "passed": 29, "warned": 0, "failed": 0, "criticalFailed": 0 }
}
```

## Known limitations

- **TLS-intercepting proxies:** If the network uses a proxy that terminates TLS (common in enterprise environments), all HTTPS and WSS checks will pass against the proxy, not the actual Postman server. If Postman still fails after all checks pass, ask the IT team whether a TLS inspection proxy is in use.
- **WSS false negatives are unlikely but possible:** A timeout on the WSS check is labeled as a potential firewall block. In rare cases, it could be caused by network congestion or server-side issues rather than a firewall. If HTTPS passes but WSS times out consistently, WebSocket-specific blocking is the most likely cause.
- **Server-side outages:** The HTTPS check reports the actual HTTP status code. A 503 or 502 from the server itself (not during a WSS upgrade) could indicate a Postman service outage rather than a network issue. Check [Postman's status page](https://status.postman.com) if many HTTPS checks return 5xx.
- **IPv6:** DNS checks use `dns.resolve4()` (IPv4 only). If the network is IPv6-only, DNS checks may fail even if the domain is resolvable via AAAA records.
- **PTY check on Windows:** Without `node-pty` installed, the PTY spawn check is skipped on Windows because there is no OS-level PTY allocation command. Install `node-pty` (`npm install node-pty`) alongside the tool for a definitive PTY test on Windows.
- **`node-pty` not bundled:** The tool does not ship `node-pty` as a dependency to avoid false positives. The native addon's `spawn-helper` binary can lose execute permissions during `npm install`, causing PTY spawn failures that are npm artifacts, not EDR blocks. If `node-pty` is already installed in the environment (e.g. from a development setup), the tool will use it automatically via dynamic import.

## Building from source

```bash
git clone https://github.com/postmanlabs/postman-connectivity-cli.git
cd postman-connectivity-cli
npm install
npm run build
node dist/bin/postman-client-diagnostics.js
```

### Building a standalone binary

```bash
bash sea/build.sh
./dist/bin-sea/postman-client-diagnostics-*
```

Requires Node.js 20+ (for Single Executable Applications support).

## License

MIT
