specshield 3.2.2 → 3.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,19 +7,24 @@
7
7
 
8
8
  ---
9
9
 
10
- > **OpenAPI Diff · API Breaking Change Detection · Swagger Diff · Bi-Directional Contract Testing · Pact Alternative · can-i-deploy · GitHub PR Checks · CI/CD**
10
+ > **OpenAPI Diff · Breaking-Change Detection · Bi-Directional Contract Testing · `can-i-deploy` Gate · Pact-File Ingest · Live-Traffic Capture · Spec-vs-Production Conformance · GitHub PR Checks**
11
11
 
12
12
  ---
13
13
 
14
- ## Stop Breaking APIs in Production
14
+ ## Never ship a breaking change to your API consumers.
15
15
 
16
- **SpecShield** detects breaking API changes, runs contract tests, and gates deployments — directly in your CI/CD pipeline before they become production incidents.
16
+ **SpecShield** is the one CLI that does four things to keep your API safe:
17
+
18
+ 1. **Diff** two OpenAPI specs and fail CI on breaking changes.
19
+ 2. **Bi-directional contract testing** with `can-i-deploy` — block a deploy that would break a consumer.
20
+ 3. **`bdct capture from-har`** — turn recorded traffic into an accurate consumer contract (no Pact DSL).
21
+ 4. **`bdct verify-provider`** — prove the running provider actually matches its OpenAPI spec.
17
22
 
18
23
  ```
19
- OpenAPI diff + contract testing + bi-directional contracts + GitHub PR checks — in one CLI.
24
+ OpenAPI diff + BDCT + HAR → consumer contract + spec-vs-production conformance — in one CLI.
20
25
  ```
21
26
 
22
- No broker. No complex setup. Works in 30 seconds.
27
+ No broker. No Pact DSL. Language-agnostic. Works in 30 seconds. Local mode never uploads your specs.
23
28
 
24
29
  ---
25
30
 
@@ -27,48 +32,36 @@ No broker. No complex setup. Works in 30 seconds.
27
32
 
28
33
  **APIs break silently. Your users feel it first.**
29
34
 
30
- - A backend developer renames a field — the mobile app crashes for 10,000 users
31
- - An endpoint gets removed — the frontend breaks with no error in your logs
32
- - A required field changes — partner integrations start failing at 2am
33
- - You only find out after a production incident, a 3am alert, or an angry customer
35
+ - A backend rename of `status` `paymentStatus` — the mobile app crashes for 10,000 users.
36
+ - An endpoint gets removed — the frontend breaks with no error in your logs.
37
+ - A required field changes — partner integrations fail at 2am.
38
+ - You find out from an angry customer, not from CI.
34
39
 
35
- Manual code review doesn't catch this. Integration tests run too late.
36
- **You need a contract between your services — and something that enforces it.**
40
+ Manual review doesn't catch it. Integration tests run too late.
41
+ **You need a contract between your services — and something that enforces it on every PR and every deploy.**
37
42
 
38
43
  ---
39
44
 
40
45
  ## The Solution
41
46
 
42
- Think of SpecShield as **unit tests for your API contracts**.
47
+ Think of SpecShield as **unit tests for your API contracts.**
43
48
 
44
- - The consumer declares what it expects from the provider
45
- - The provider proves it satisfies those expectations before deploying
46
- - SpecShield blocks the deployment if anything breaks
49
+ - Consumers declare (or record) what they expect from the provider.
50
+ - The provider proves it satisfies those expectations before deploying.
51
+ - The pipeline blocks the deploy if anything breaks.
47
52
 
48
53
  ```
49
- Consumer publishes contract → Provider verifies it → CI either passes or blocks
54
+ Consumer publishes contract → Provider verifies it → CI passes or blocks
50
55
  ```
51
56
 
52
57
  No runtime surprise. No production incident. No 3am page.
53
58
 
54
59
  ---
55
60
 
56
- ## 🎥 Demo
57
-
58
- [![SpecShield Demo — API Breaking Change Detection & Contract Testing](https://img.youtube.com/vi/mugDyQQGqZw/maxresdefault.jpg)](https://www.youtube.com/watch?v=mugDyQQGqZw)
59
-
60
- > Watch: Catching a breaking API change before it hits production — compare specs, publish provider/consumer specs via BDCT, and gate deployments with `can-i-deploy`.
61
-
62
- **CLI preview:**
63
-
64
- ![SpecShield CLI Demo](https://raw.githubusercontent.com/specshield26/specshield-cli/main/demo/specshield-demo.gif)
65
-
66
- > `specshield compare payment-api-v1.yaml payment-api-v2.yaml --fail-on-breaking`
67
-
68
- ---
69
-
70
61
  ## See It in Action
71
62
 
63
+ **Catch a breaking spec change in CI:**
64
+
72
65
  ```bash
73
66
  $ specshield compare base.yaml target.yaml --fail-on-breaking
74
67
 
@@ -85,37 +78,43 @@ $ specshield compare base.yaml target.yaml --fail-on-breaking
85
78
  CI Result: FAILED · Exit code: 1
86
79
  ```
87
80
 
88
- **That 3-line output just saved you a production incident.**
89
-
90
- And when everything is safe:
81
+ **Catch the running provider drifting from its own spec:**
91
82
 
92
83
  ```bash
93
- $ specshield compare base.yaml target.yaml --fail-on-breaking
84
+ $ specshield bdct verify-provider --spec api/openapi.yaml --base-url https://staging.payments.acme.com
94
85
 
95
- NO BREAKING CHANGES FOUND
86
+ Provider conformance spec vs https://staging.payments.acme.com
87
+ ────────────────────────────────────────────────────────────
88
+ PASS GET /health (200)
89
+ FAIL GET /payments/{paymentId} (200)
90
+ /amount: must be number
91
+ PASS GET /orders (200)
92
+ ────────────────────────────────────────────────────────────
93
+ 2 pass · 1 fail · 0 error · 0 skip (3 probes)
94
+ ```
96
95
 
97
- Breaking changes : 0
98
- Modifications : 0
99
- Additions : 3
96
+ **Turn recorded test traffic into an accurate consumer contract — no Pact DSL:**
100
97
 
101
- CI Result: PASSED · Exit code: 0
98
+ ```bash
99
+ $ specshield bdct capture from-har --in checkout-tests.har --base-url https://api.acme.com --out consumer.yaml
100
+
101
+ ✔ Wrote consumer.yaml (2 endpoints, 3 ops from 17/24 entries)
102
102
  ```
103
103
 
104
+ **That output just saved you a production incident.**
105
+
104
106
  ---
105
107
 
106
108
  ## Quick Start
107
109
 
108
- No account. No login. No config file. Just run it.
110
+ No account, no login, no config file for `compare`. Just run it.
109
111
 
110
112
  ```bash
111
113
  npm install -g specshield
112
- ```
113
-
114
- ```bash
115
114
  specshield compare base.yaml target.yaml --fail-on-breaking
116
115
  ```
117
116
 
118
- That's it. Works with any OpenAPI 3.x YAML or JSON spec.
117
+ Works with any OpenAPI 3.x YAML or JSON.
119
118
 
120
119
  **For BDCT**, run the wizard once and never re-type the same flags again:
121
120
 
@@ -123,71 +122,24 @@ That's it. Works with any OpenAPI 3.x YAML or JSON spec.
123
122
  specshield init
124
123
  ```
125
124
 
126
- It autodetects your OpenAPI spec, your service name (from `package.json` /
127
- `pyproject.toml` / `pom.xml` / `go.mod`), your git branch, and your
128
- default environment, asks you a few questions, and writes a
129
- **`.specshield.yml`** at the project root. Every subsequent
130
- `specshield bdct ...` invocation reads this file, so your CI commands
131
- collapse to:
125
+ It auto-detects your spec, service name (from `package.json` / `pyproject.toml` / `pom.xml` / `go.mod`), git branch, and environment; writes **`.specshield.yml`**; and (optionally) seeds a GitHub Actions workflow. Every subsequent `specshield bdct …` invocation reads this file, so your CI commands collapse to:
132
126
 
133
127
  ```bash
134
128
  specshield bdct publish-provider --version $GITHUB_SHA
135
- specshield bdct can-i-deploy --version $GITHUB_SHA
129
+ specshield bdct can-i-deploy --version $GITHUB_SHA
136
130
  ```
137
131
 
138
- See specshield init](#specshield-init--first-run-setup-wizard) below.
139
-
140
- > **Quiet install for CI / Docker images:**
141
- > The post-install welcome banner auto-detects CI environments (`CI`,
142
- > `GITHUB_ACTIONS`, `BUILDKITE`, `CIRCLECI`, `GITLAB_CI`, `JENKINS_URL`,
143
- > `TRAVIS`, `TF_BUILD`) and skips itself there — so your CI logs stay clean.
144
- > To silence it on a workstation too:
145
- > ```bash
146
- > export SPECSHIELD_NO_BANNER=1
147
- > ```
148
-
149
- ---
150
-
151
- ## 🚀 Create Your Free Account
152
-
153
- **Unlock history, dashboards, contract testing, and team features.**
154
-
155
- 👉 **[Create free account at specshield.io](https://specshield.io)**
156
-
157
- - ✅ No credit card required
158
- - ✅ Takes less than 30 seconds
159
- - ✅ GitHub and Google sign-in supported
160
- - ✅ Local compare always free, forever
161
-
162
- ---
163
-
164
- ## Local vs Cloud
165
-
166
- | Feature | Local (Free) | Cloud Free | Cloud Pro |
167
- |---|---|---|---|
168
- | Compare two spec files | ✅ | ✅ | ✅ |
169
- | Breaking change detection | ✅ | ✅ | ✅ |
170
- | JSON / human output | ✅ | ✅ | ✅ |
171
- | Fail CI on breaking change | ✅ | ✅ | ✅ |
172
- | **`specshield history` — compare timeline** | ❌ | ✅ | ✅ |
173
- | **`specshield share` — public report URLs** | ❌ | ✅ | ✅ |
174
- | **Dashboard** | ❌ | ✅ | ✅ |
175
- | **GitHub App PR checks** | ❌ | ✅ | ✅ |
176
- | **BDCT bi-directional contracts** | ❌ | ❌ | ✅ |
177
- | **BDCT can-i-deploy gating** | ❌ | ❌ | ✅ |
178
- | **BDCT compatibility matrix** | ❌ | ❌ | ✅ |
179
- | **Team collaboration** | ❌ | ❌ | ✅ |
180
- | **API drift trends** | ❌ | ✅ | ✅ |
132
+ > **Quiet install for CI / Docker:** the post-install welcome banner auto-skips in CI (`CI`, `GITHUB_ACTIONS`, `BUILDKITE`, `CIRCLECI`, `GITLAB_CI`, `JENKINS_URL`, `TRAVIS`, `TF_BUILD`). To silence it on a workstation too: `export SPECSHIELD_NO_BANNER=1`.
181
133
 
182
134
  ---
183
135
 
184
136
  ## A Real-World Story
185
137
 
186
- > The `payment-service` team merged a change that renamed `status` to `paymentStatus`
187
- > in the payment creation response. The `checkout-ui` team wasn't notified.
138
+ > The `payment-service` team merged a change that renamed `status` to
139
+ > `paymentStatus`. The `checkout-ui` team wasn't notified.
188
140
  >
189
- > Without SpecShield, this would have reached staging, broken checkout for every user,
190
- > and triggered an incident at 2am.
141
+ > Without SpecShield, this would have reached staging, broken checkout for
142
+ > every user, and triggered an incident at 2am.
191
143
  >
192
144
  > With SpecShield, the provider's CI published the new spec via
193
145
  > `specshield bdct publish-provider`. The compatibility engine ran against
@@ -205,92 +157,40 @@ See [§ specshield init](#specshield-init--first-run-setup-wizard) below.
205
157
 
206
158
  ## Use Cases
207
159
 
208
- **Pull Request Validation**
209
- Catch breaking changes before they're merged. Run `specshield compare` against your base branch on every PR.
210
-
211
- **CI/CD Gating**
212
- Fail the build automatically when a breaking change is detected. Exit code `1` triggers your pipeline to stop.
213
-
214
- **Microservices Contract Safety**
215
- Consumer teams publish what they expect. Provider teams verify they deliver it. No cross-team surprises.
216
-
217
- **API Governance**
218
- Track API drift over time across your entire platform. Know what changed, when, and which team changed it.
160
+ - **Pull-request validation** — catch breaking changes before merge with `specshield compare`.
161
+ - **CI/CD gating** exit code `1` stops the pipeline automatically.
162
+ - **Microservices contract safety** — consumers publish what they expect, providers verify they deliver it, no cross-team surprises.
163
+ - **API governance** — track API drift over time across your platform; know what changed, when, by whom.
164
+ - **Provider conformance** *(new)* make sure your *running* service actually matches its published OpenAPI spec, not just on paper.
165
+ - **Pact-free consumer contracts** *(new)* — record real traffic with `capture from-har` and get an OpenAPI consumer contract without writing a single line of Pact DSL.
219
166
 
220
167
  ---
221
168
 
222
- ## vs. Alternatives
169
+ ## Local vs Cloud
223
170
 
224
- | Feature | SpecShield | Pact / Pactflow | openapi-diff |
171
+ | Feature | Local (Free) | Team | Enterprise |
225
172
  |---|---|---|---|
226
- | OpenAPI / Swagger native | ✅ | (code-level) | ✅ |
227
- | No broker required | ✅ | (needs Pact Broker) | ✅ |
228
- | **Bi-directional contract testing** | ✅ | ✅ (Pactflow paid) | |
229
- | Breaking change detection | ✅ | | ✅ |
230
- | can-i-deploy gating | ✅ | ✅ (via broker) | |
231
- | **GitHub App PR checks** | ✅ | | |
232
- | **Pact JSON contract import** | | ✅ | |
233
- | Hosted dashboard | | ✅ (Pactflow, paid) | |
234
- | Team collaboration | | ✅ (paid) | |
235
- | CLI-first workflow | | ❌ | ✅ |
236
- | Free hosted tier | | ❌ | N/A |
237
-
238
- ---
239
-
240
- ## Pricing
241
-
242
- | Plan | Price | What's included |
243
- |---|---|---|
244
- | **Free** | $0 forever | Local compare (unlimited) · Compare history & dashboard · GitHub App PR checks |
245
- | **Pro** | Coming soon | Everything in Free + BDCT bi-directional contracts · BDCT can-i-deploy & matrix · Team collaboration · Advanced reporting · Priority support |
246
-
247
- No credit card ever required for the free plan.
248
- **[Get started free →](https://specshield.io)**
173
+ | Compare two spec files | ✅ | | ✅ |
174
+ | Breaking change detection | ✅ | | ✅ |
175
+ | JSON / human output | ✅ | ✅ | |
176
+ | Fail CI on breaking change | ✅ | | ✅ |
177
+ | **`bdct capture from-har`** | ✅ | ✅ | |
178
+ | **`bdct verify-provider`** | ✅ | | |
179
+ | `specshield history` compare timeline | | ✅ | |
180
+ | `specshield share` — public report URLs | | ✅ | |
181
+ | Hosted dashboard | | ✅ | |
182
+ | GitHub App PR checks | ❌ | ✅ | ✅ |
183
+ | BDCT consumer registry + can-i-deploy gate | ❌ | | ✅ |
184
+ | BDCT compatibility matrix | ❌ | ✅ | ✅ |
185
+ | Team workspace + audit log + RBAC | ❌ | ✅ | ✅ |
186
+ | Slack notifications | ❌ | ✅ | ✅ |
187
+ | SAML SSO, SCIM, on-prem | ❌ | ❌ | ✅ |
188
+
189
+ > **The local-only commands are first-class.** `compare`, `bdct capture from-har`, and `bdct verify-provider` all run entirely in your CI — your spec text never leaves your infrastructure.
249
190
 
250
191
  ---
251
192
 
252
- ## Login & Authentication
253
-
254
- **Step 1 — Create your account**
255
-
256
- Go to [https://specshield.io](https://specshield.io) · Sign in with GitHub or Google (30 seconds)
257
-
258
- **Step 2 — Generate an API key**
259
-
260
- Dashboard → **API Keys** → **Generate Key** · Copy the `ss_` token
261
-
262
- **Step 3 — Authenticate the CLI**
263
-
264
- ```bash
265
- specshield login --api-key ss_your_token_here
266
- ```
267
-
268
- ```
269
- ✔ Logged in successfully.
270
- Customer : Your Name
271
- Plan : FREE
272
- ```
273
-
274
- Done. Your token is stored in `~/.specshield/config.json` — no need to pass it on every command.
275
-
276
- **Or use an environment variable (recommended for CI):**
277
-
278
- ```bash
279
- export SPECSHIELD_API_KEY=ss_your_token_here
280
- ```
281
-
282
- This single env var works for every command — `compare`, `login`, and every
283
- `bdct` subcommand all read it.
284
-
285
- Token resolution order:
286
- - `compare` / `login`: `--api-key` flag → `SPECSHIELD_API_KEY` env var → stored config → `.specshield.yml`
287
- - `bdct ...` subcommands: `--api-token` flag → `SPECSHIELD_API_KEY` env var → stored config
288
-
289
- > **Why two flag names?** `compare` and `login` were built first and used
290
- > `--api-key`. The newer `bdct` commands use `--api-token` to keep the word
291
- > "key" reserved for the stored config concept. The env var unifies both.
292
-
293
- ---
193
+ # Commands
294
194
 
295
195
  ## Local Compare
296
196
 
@@ -313,36 +213,24 @@ specshield compare base.yaml target.yaml --output result.json
313
213
  specshield compare base.yaml target.yaml --ignore "DELETE /admin removed" --fail-on-breaking
314
214
  ```
315
215
 
316
- ---
317
-
318
216
  ## Remote Compare
319
217
 
320
- Sends your specs to SpecShield and stores results in your dashboard.
321
- Requires a free account and API key.
218
+ Sends your specs to SpecShield and stores results in your dashboard. Requires a free account.
322
219
 
323
220
  ```bash
324
- # Remote compare
325
221
  specshield compare base.yaml target.yaml --remote
326
-
327
- # Remote + fail on breaking
328
222
  specshield compare base.yaml target.yaml --remote --fail-on-breaking
329
-
330
- # Remote + JSON output saved to file
331
223
  specshield compare base.yaml target.yaml --remote --json --output result.json
332
224
  ```
333
225
 
334
- ---
335
-
336
226
  ## Comparison History
337
227
 
338
- Every `specshield compare --remote` is saved to your SpecShield account.
339
- List the recent comparisons your account has run from any machine — useful
340
- for tracking API drift over time across CI pipelines + local runs.
228
+ Every `specshield compare --remote` is saved to your account. List recent comparisons from any machine — useful for tracking API drift over time across CI pipelines + local runs.
341
229
 
342
230
  ```bash
343
231
  specshield history # last 20 comparisons
344
- specshield history --limit 50 # show more
345
- specshield history --json # machine-readable for scripts
232
+ specshield history --limit 50
233
+ specshield history --json
346
234
  ```
347
235
 
348
236
  ```
@@ -353,15 +241,9 @@ specshield history --json # machine-readable for scripts
353
241
  480 7 breaking 2026-05-16 18:55 billing-v3.yaml → billing-v4.yaml
354
242
  ```
355
243
 
356
- Account required — run `specshield login` to set up (free, no credit card).
357
-
358
- ---
359
-
360
244
  ## Share a Comparison
361
245
 
362
- Generate a public, tokenized URL for any comparison report. Anyone with
363
- the link can view the diff — no SpecShield account needed. Great for
364
- pasting into Slack threads, PR comments, or Jira tickets.
246
+ Generate a public, tokenized URL for any comparison report. Great for Slack threads, PR comments, Jira tickets.
365
247
 
366
248
  ```bash
367
249
  # Share an existing report by ID (from `specshield history`)
@@ -376,190 +258,45 @@ specshield share 482 --expires 30
376
258
 
377
259
  ```
378
260
  ✔ Share link ready
379
- ─────────────────────────────────────────────────────
380
261
  https://specshield.io/r/_Ru8OVubxY3r9zHOsylESaULphCqBYH5jTPYldSMU88
381
262
  Expires: 2026-06-16T12:34:56Z
382
-
383
- Anyone with this link can view the diff — no SpecShield account required.
384
263
  ```
385
264
 
386
- Links use a 256-bit random token, so they can't be guessed by enumeration.
387
- Revoke any time from your dashboard at [specshield.io](https://specshield.io).
265
+ Links use a 256-bit random token (unguessable by enumeration). Revoke any time from the dashboard.
388
266
 
389
- Account required`specshield login` to set up.
390
-
391
- ---
392
-
393
- ## GitHub Integration
267
+ ## GitHub App PR Checks
394
268
 
395
269
  **Automatic API contract checks on every pull request — no workflow YAML required.**
396
270
 
397
- Install the SpecShield GitHub App once and every PR that touches your OpenAPI spec gets:
398
- - A GitHub check run (pass/fail) visible directly on the PR
399
- - A PR comment with the full diff table — breaking changes highlighted in red
400
- - Configurable `fail-on-breaking` per repository
401
-
402
- ### Install the GitHub App
403
-
404
- 1. Go to **Dashboard → GitHub Integration** at [specshield.io](https://specshield.io)
405
- 2. Click **Install GitHub App**
406
- 3. Choose the repositories to enable (or select all)
407
- 4. Done — no secrets, no workflow changes needed
408
-
409
- ### How It Works
410
-
411
- When a PR is opened or updated, SpecShield:
412
-
413
- 1. Fetches the OpenAPI spec from the base branch and the PR branch
414
- 2. Runs the same diff engine as `specshield compare`
415
- 3. Posts a GitHub check run — **Passed** if no breaking changes, **Failed** if breaking changes found
416
- 4. Adds a PR comment with the full breakdown:
417
-
418
- ```
419
- ## SpecShield API Contract Check
420
-
421
- | Change | Type | Severity |
422
- |--------|------|----------|
423
- | POST /payments — "amount" required field added | Request schema | BREAKING |
424
- | GET /orders/{id} — "status" type changed | Response schema | BREAKING |
425
- | GET /users — new query param "filter" | Addition | NON-BREAKING |
426
-
427
- Breaking changes: 2 · Non-breaking: 1
428
- ```
429
-
430
- ### Configure Per Repository
431
-
432
- Add a `.specshield.yml` to your repo root:
433
-
434
- ```yaml
435
- github:
436
- specPath: api/openapi.yaml # path to your spec (default: openapi.yaml)
437
- failOnBreaking: true # block PR merge on breaking changes (default: true)
438
- commentOnPr: true # post breakdown comment (default: true)
439
- ```
440
-
441
- ### Requirements
442
-
443
- - The spec file must exist on both the base branch and the PR branch
444
- - Supported formats: OpenAPI 3.x YAML or JSON
445
- - The GitHub App needs `pull_requests: write` and `checks: write` permissions (granted during install)
271
+ Install once at **Dashboard → GitHub Integration**, choose your repos, done. Every PR that touches the OpenAPI spec gets:
446
272
 
447
- ---
448
-
449
- ## `specshield init` first-run setup wizard
273
+ - A GitHub check run (pass/fail) visible on the PR
274
+ - A sticky PR comment with the full diff (breaking changes highlighted)
275
+ - Configurable `failOnBreaking` per repository
450
276
 
451
- Run once at the root of your project. The wizard:
452
-
453
- 1. Detects your OpenAPI spec (looks under `api/`, `spec/`, `docs/`, repo root).
454
- 2. Detects your service name (`package.json`, `pyproject.toml`, `pom.xml`, `go.mod`, `Cargo.toml`, or directory name).
455
- 3. Detects your git branch and suggests `production` for `main`/`master`, `staging` otherwise.
456
- 4. Asks whether this project is a provider, a consumer, or both.
457
- 5. Asks for your org key (autocompletes from your account if you're already signed in).
458
- 6. Validates / stores your API key.
459
- 7. Writes **`.specshield.yml`** and, optionally, a starter **`.github/workflows/specshield-bdct.yml`** that uses [`specshield26/bdct-action@v1`](https://github.com/marketplace/actions/specshield-bdct).
460
-
461
- ```bash
462
- specshield init
463
- ```
464
-
465
- ### Example `.specshield.yml`
277
+ Configure per-repo via `.specshield.yml`:
466
278
 
467
279
  ```yaml
468
- schemaVersion: 1
469
-
470
- failOnBreaking: true
471
- severity: error
472
-
473
- bdct:
474
- org: acme-pay
475
- environment: staging
476
-
477
- provider:
478
- name: payment-service
479
- spec: api/openapi.yaml
480
-
481
- # consumer (optional — present when --kind=consumer or --kind=both):
482
- # consumer:
483
- # name: checkout-ui
484
- # provider: payment-service
485
- # contract: contracts/payment-service.yaml
486
- # format: OPENAPI
487
-
488
280
  github:
489
281
  specPath: api/openapi.yaml
490
282
  failOnBreaking: true
491
283
  commentOnPr: true
492
284
  ```
493
285
 
494
- CLI flags **always** override this file. Paths in the file are resolved
495
- relative to the file's own directory, so you can run `specshield bdct ...`
496
- from any subdirectory of your project.
497
-
498
- ### Non-interactive (scriptable) mode
499
-
500
- ```bash
501
- # Provider-only project
502
- specshield init --no-interactive \
503
- --kind provider \
504
- --org acme-pay \
505
- --provider payment-service \
506
- --spec api/openapi.yaml \
507
- --env staging \
508
- --write-workflow
509
-
510
- # Consumer-only project
511
- specshield init --no-interactive \
512
- --kind consumer \
513
- --org acme-pay \
514
- --consumer checkout-ui \
515
- --consumer-provider payment-service \
516
- --contract contracts/payment-service.yaml \
517
- --format OPENAPI \
518
- --env staging
519
- ```
520
-
521
- ### All `init` flags
522
-
523
- | Flag | Purpose |
524
- | --- | --- |
525
- | `--no-interactive` | Run without prompts; all required fields must come from flags or `package.json`/git autodetection. |
526
- | `--print` | Detect everything, print the proposed YAML to stdout, write nothing. Good for `--dry-run` review in CI. |
527
- | `--force` | Skip the overwrite-confirmation if `.specshield.yml` already exists. |
528
- | `--server <url>` | Use a non-default SpecShield endpoint (self-hosted / staging). Default: `https://specshield.io`. |
529
- | `--kind <kind>` | `provider`, `consumer`, `both`, or `skip`. Required in `--no-interactive`. |
530
- | `--org <key>` | Organization key. |
531
- | `--provider <name>` | Provider service name (used when `--kind=provider|both`). |
532
- | `--spec <path>` | Path to the provider OpenAPI spec. |
533
- | `--consumer <name>` | Consumer service name (used when `--kind=consumer|both`). |
534
- | `--consumer-provider <name>` | The provider this consumer talks to (used when `--kind=consumer|both`). |
535
- | `--contract <path>` | Path to the consumer contract (OpenAPI or Pact JSON). |
536
- | `--format <fmt>` | Consumer contract format: `OPENAPI` (default) or `PACT`. |
537
- | `--env <environment>` | Default environment for BDCT operations. |
538
- | `--write-workflow` | Also write a starter GitHub Actions workflow under `.github/workflows/specshield-bdct.yml`. |
539
-
540
- > **What is _not_ written into `.specshield.yml`:** your API key. It is
541
- > stored in `~/.specshield/config.json` (set by `specshield login`) or
542
- > read from `SPECSHIELD_API_KEY` in CI. The project file is meant to be
543
- > committed; never commit a secret into it.
544
-
545
286
  ---
546
287
 
547
- ## Bi-Directional Contract Testing (BDCT)
288
+ # Bi-Directional Contract Testing (BDCT)
548
289
 
549
- **Spec-to-spec contract testing no running services required.**
290
+ **Compatibility without Pact's broker overhead or DSL.**
550
291
 
551
- Both sides publish their OpenAPI specs. SpecShield compares them and flags
552
- mismatches immediately — ideal for teams that don't run services locally or in
553
- CI. Pact JSON consumer contracts are also accepted and auto-converted.
554
-
555
- > BDCT requires a **Pro plan**. [Upgrade at specshield.io/upgrade](https://specshield.io/upgrade)
292
+ Both sides publish what they have — provider publishes its OpenAPI spec, consumer publishes either an OpenAPI subset *or* a Pact JSON file — and SpecShield's engine verifies that every endpoint/field the consumer relies on is satisfied by the provider. `can-i-deploy` then gates the deploy.
556
293
 
557
294
  ### How BDCT Works
558
295
 
559
- 1. Consumer team publishes an OpenAPI spec subset (the endpoints they use)
560
- 2. Provider team publishes their full OpenAPI spec
561
- 3. SpecShield compares them: endpoint presence, request schemas, response fields, status codes, types
562
- 4. `can-i-deploy` gates the deployment — returns `0` only when all consumers are compatible
296
+ 1. Consumer publishes a contract (OpenAPI subset *or* Pact JSON).
297
+ 2. Provider publishes its full OpenAPI spec.
298
+ 3. SpecShield compares: endpoint presence, request schemas, response fields, status codes, types.
299
+ 4. `can-i-deploy` returns `0` only when all consumers are compatible.
563
300
 
564
301
  ### Publish a Provider Spec
565
302
 
@@ -573,31 +310,16 @@ specshield bdct publish-provider \
573
310
  --branch main
574
311
  ```
575
312
 
576
- ```
577
- ✔ Provider Spec Published
578
- ─────────────────────────────────────────────────────
579
- ID : 1842
580
- Provider : payment-service
581
- Version : v2.1.0
582
- Environment : production
583
- Published At: 2026-05-13 14:30:12
584
- Verifications triggered: 3
585
- ```
586
-
587
- `--branch` is optional — when present it's stamped on the published spec so you
588
- can correlate a spec version with the git branch it came from in
589
- `bdct list-providers`.
313
+ `--branch` is optional — stamped on the published spec so you can correlate a spec version with the git branch it came from in `bdct list-providers`.
590
314
 
591
- ### Publish a Consumer Contract
315
+ ### Publish a Consumer Contract (OpenAPI subset or Pact JSON)
592
316
 
593
- The consumer contract is an OpenAPI spec that describes only the endpoints the consumer uses:
317
+ The consumer contract is an OpenAPI subset describing only the endpoints the consumer uses:
594
318
 
595
319
  ```yaml
596
320
  # consumer-contract.yaml — only the subset checkout-ui uses
597
321
  openapi: "3.0.0"
598
- info:
599
- title: checkout-ui → payment-service contract
600
- version: "1.0.0"
322
+ info: { title: checkout-ui → payment-service contract, version: "1.0.0" }
601
323
  paths:
602
324
  /payments:
603
325
  post:
@@ -608,8 +330,8 @@ paths:
608
330
  type: object
609
331
  required: [orderId, amount, currency]
610
332
  properties:
611
- orderId: { type: string }
612
- amount: { type: number }
333
+ orderId: { type: string }
334
+ amount: { type: number }
613
335
  currency: { type: string }
614
336
  responses:
615
337
  "201":
@@ -632,26 +354,7 @@ specshield bdct publish-consumer \
632
354
  --format OPENAPI
633
355
  ```
634
356
 
635
- ```
636
- ✔ Consumer Contract Published
637
- ─────────────────────────────────────────────────────
638
- ID : 942
639
- Consumer : checkout-ui
640
- Provider : payment-service
641
- Version : 2.0.0
642
- Format : OPENAPI
643
- Published At: 2026-05-13 14:31:05
644
- Verifications triggered: 1
645
-
646
- ➜ Run: specshield bdct verify --consumer checkout-ui --provider payment-service
647
- ```
648
-
649
- If the provider spec is already published, compatibility is verified
650
- automatically — the "Verifications triggered" count reflects that.
651
-
652
- **Pact JSON contracts are also supported** — pass `--format PACT` and point
653
- `--contract` at a Pact file. The backend stores the format and runs the same
654
- compatibility engine against your provider's OpenAPI spec:
357
+ **Pact JSON contracts are also supported** — pass `--format PACT` and point `--contract` at a Pact file. The same compatibility engine runs against your provider's OpenAPI spec:
655
358
 
656
359
  ```bash
657
360
  specshield bdct publish-consumer \
@@ -667,28 +370,14 @@ specshield bdct publish-consumer \
667
370
 
668
371
  ### Verify Compatibility
669
372
 
670
- Manually trigger a verification between a specific consumer/provider pair:
671
-
672
373
  ```bash
673
374
  specshield bdct verify \
674
375
  --org acme-store \
675
- --consumer checkout-ui \
676
- --consumer-version 2.0.0 \
677
- --provider payment-service \
678
- --provider-version v2.1.0 \
376
+ --consumer checkout-ui --consumer-version 2.0.0 \
377
+ --provider payment-service --provider-version v2.1.0 \
679
378
  --env production
680
379
  ```
681
380
 
682
- Compatible output:
683
- ```
684
- ✔ COMPATIBLE
685
-
686
- Endpoints checked: 2
687
- Compatible : 2
688
- Incompatible : 0
689
- ```
690
-
691
- Incompatible output:
692
381
  ```
693
382
  ✖ INCOMPATIBLE
694
383
 
@@ -706,24 +395,18 @@ Incompatible output:
706
395
  consumer: integer → provider: string
707
396
  ```
708
397
 
709
- ### Can I Deploy? (BDCT)
398
+ ### Can I Deploy?
710
399
 
711
400
  ```bash
712
401
  specshield bdct can-i-deploy \
713
- --org acme-store \
714
- --service payment-service \
715
- --version v2.1.0 \
716
- --env production
402
+ --org acme-store --service payment-service \
403
+ --version v2.1.0 --env production
717
404
  ```
718
405
 
719
- Deployable output:
720
-
721
406
  ```
722
407
  ✔ PASS: payment-service v2.1.0 is deployable in production
723
408
  ─────────────────────────────────────────────────────
724
409
 
725
- Consumer Verifications
726
-
727
410
  Consumer Version Status Verified At
728
411
  ─────────── ─────── ────────── ───────────────
729
412
  checkout-ui 2.0.0 COMPATIBLE 2026-05-13 14:32
@@ -731,100 +414,52 @@ Deployable output:
731
414
  partner-sdk 3.2.1 COMPATIBLE 2026-05-13 14:32
732
415
  ```
733
416
 
734
- Blocked output:
735
-
736
- ```
737
- ✖ FAIL: payment-service v2.1.0 is NOT deployable in production
738
- ─────────────────────────────────────────────────────
739
-
740
- Consumer Verifications
741
-
742
- Consumer Version Status Verified At
743
- ─────────── ─────── ──────────── ───────────────
744
- checkout-ui 2.0.0 INCOMPATIBLE 2026-05-13 14:32
745
- mobile-app 1.5.0 INCOMPATIBLE 2026-05-13 14:32
746
-
747
- ➜ Run: specshield bdct verify --consumer <NAME> --provider payment-service
748
- ➜ to identify and resolve incompatibilities
749
- ```
750
-
751
- Exit codes: `0` = deployable · `1` = blocked · `2` = error
417
+ Exit codes: `0` = deployable · `1` = blocked · `2` = error.
752
418
 
753
419
  ### Compatibility Matrix
754
420
 
755
- View the compatibility status across all consumer/provider pairs in your org:
756
-
757
421
  ```bash
758
422
  specshield bdct matrix --org acme-store --env production
759
423
  ```
760
424
 
761
425
  ```
762
- BDCT Compatibility Matrix
763
- Environment: production
764
- ─────────────────────────────────────────────────────
765
-
766
426
  Consumer \ Provider payment-service order-service
767
427
  ─────────────────── ─────────────── ─────────────
768
428
  checkout-ui COMPATIBLE COMPATIBLE
769
429
  mobile-app INCOMPATIBLE COMPATIBLE
770
430
  partner-sdk COMPATIBLE UNKNOWN
771
-
772
- ■ COMPATIBLE ■ INCOMPATIBLE ■ UNKNOWN
773
431
  ```
774
432
 
775
- ### List Provider Specs
433
+ ### List Provider Specs / Consumer Contracts / Verifications
776
434
 
777
435
  ```bash
778
- # All providers for the org
779
436
  specshield bdct list-providers --org acme-store
780
-
781
- # Filter by provider name
782
437
  specshield bdct list-providers --org acme-store --provider payment-service
783
- ```
784
-
785
- ```
786
- Published Provider Specs
787
- ─────────────────────────────────────────────────────
788
- Showing 3 of 3 specs
789
-
790
- ID Provider Version Environment Branch Published At
791
- ──── ─────────────── ─────── ─────────── ────── ───────────────
792
- 1842 payment-service v2.1.0 production main 2026-05-13 14:30
793
- 1799 payment-service v2.0.0 staging main 2026-04-20 09:11
794
- 1772 order-service v1.3.0 production main 2026-04-28 11:02
795
- ```
796
-
797
- ### List Consumer Contracts
798
438
 
799
- ```bash
800
- # All consumers for the org
801
439
  specshield bdct list-consumers --org acme-store
802
-
803
- # Filter by consumer or provider
804
440
  specshield bdct list-consumers --org acme-store --consumer checkout-ui
805
- specshield bdct list-consumers --org acme-store --provider payment-service
441
+
442
+ specshield bdct list --org acme-store --provider payment-service --env production
806
443
  ```
807
444
 
808
- ### List Verifications
445
+ ### Full BDCT Workflow
809
446
 
810
447
  ```bash
811
- specshield bdct list \
812
- --org acme-store \
813
- --provider payment-service \
814
- --env production \
815
- --page 0 \
816
- --size 20
817
- ```
448
+ # 1. Provider publishes spec on every release
449
+ specshield bdct publish-provider \
450
+ --org acme-store --provider payment-service \
451
+ --version v2.1.0 --spec ./api/openapi.yaml
818
452
 
819
- ```
820
- BDCT Verification History
821
- ─────────────────────────────────────────────────────
822
- Showing 2 of 2 verifications
453
+ # 2. Each consumer publishes their contract (update on contract change)
454
+ specshield bdct publish-consumer \
455
+ --org acme-store --consumer checkout-ui \
456
+ --provider payment-service --version 2.0.0 \
457
+ --contract ./contracts/checkout-ui.yaml
823
458
 
824
- ID Consumer Provider Cons Ver Prov Ver Status Environment Verified At
825
- ──── ─────────── ─────────────── ──────── ──────── ──────────── ─────────── ───────────────
826
- 3081 checkout-ui payment-service 2.0.0 v2.1.0 COMPATIBLE production 2026-05-13 14:32
827
- 3082 mobile-app payment-service 1.5.0 v2.1.0 INCOMPATIBLE production 2026-05-13 14:32
459
+ # 3. Gate the provider deployment
460
+ specshield bdct can-i-deploy \
461
+ --org acme-store --service payment-service \
462
+ --version v2.1.0 --env production
828
463
  ```
829
464
 
830
465
  ### BDCT JSON Output
@@ -841,48 +476,174 @@ specshield bdct can-i-deploy --org acme-store --service payment-service --versio
841
476
  "service": "payment-service",
842
477
  "version": "v2.1.0",
843
478
  "environment": "production",
844
- "reason": "payment-service v2.1.0 is INCOMPATIBLE with: checkout-ui@2.0.0 (INCOMPATIBLE)",
479
+ "reason": "payment-service v2.1.0 is INCOMPATIBLE with: checkout-ui@2.0.0",
845
480
  "verifications": [
846
- {
847
- "consumerName": "checkout-ui",
848
- "consumerVersion": "2.0.0",
849
- "status": "INCOMPATIBLE",
850
- "compatibleCount": 1,
851
- "incompatibleCount": 1
852
- }
481
+ { "consumerName": "checkout-ui", "consumerVersion": "2.0.0", "status": "INCOMPATIBLE",
482
+ "compatibleCount": 1, "incompatibleCount": 1 }
853
483
  ]
854
484
  }
855
485
  ```
856
486
 
857
- ### Full BDCT Workflow
487
+ ---
488
+
489
+ # `bdct capture from-har` — record real traffic, get a consumer contract
490
+
491
+ **The Pact alternative for teams that don't want to adopt a DSL.**
492
+
493
+ `from-har` reads a [HAR file](https://en.wikipedia.org/wiki/HAR_(file_format)) (HTTP Archive — any browser, Cypress, Playwright, k6, Insomnia, or Charles Proxy can export one), filters to your provider's host, infers an OpenAPI 3.0 consumer-contract subset from what your tests actually called, and writes it to a file you can publish with `bdct publish-consumer`.
494
+
495
+ Why this matters: a hand-written consumer subset can be wrong (you forget the `currency` field your code reads, and the provider can remove it without anyone noticing). A HAR recording **captures what your code actually does** — without the Pact DSL, language-agnostic, runs anywhere.
496
+
497
+ ### Generate a contract from a recorded test run
858
498
 
859
499
  ```bash
860
- # 1. Provider publishes spec on every release
861
- specshield bdct publish-provider \
862
- --org acme-store --provider payment-service \
863
- --version v2.1.0 --spec ./api/openapi.yaml
500
+ # Record a HAR (one of many ways)
501
+ # - Chrome DevTools → Network → right-click → "Save all as HAR"
502
+ # - Playwright: page.on('request')... or use BROWSER_TOOLS_HAR
503
+ # - Cypress: cy.intercept(...) + plugins like cypress-har-generator
504
+ # - k6: k6 run --out har=run.har script.js
864
505
 
865
- # 2. Each consumer publishes their contract once (update on contract change)
506
+ specshield bdct capture from-har \
507
+ --in tests/checkout-run.har \
508
+ --base-url https://api.acme.com \
509
+ --out contracts/checkout-ui-payment.yaml
510
+ ```
511
+
512
+ ```
513
+ ✔ Wrote contracts/checkout-ui-payment.yaml (2 endpoints, 3 ops from 17/24 entries)
514
+ ```
515
+
516
+ ### Options
517
+
518
+ | Flag | Purpose |
519
+ |---|---|
520
+ | `--in <path>` | **Required.** Input HAR file (HTTP Archive 1.2). |
521
+ | `--out <path>` | Output file (default: stdout). |
522
+ | `--base-url <url>` | Keep only entries matching this URL prefix (e.g. `https://api.acme.com` or `https://api.acme.com/v1`). |
523
+ | `--method <verbs>` | Comma-separated methods to include (e.g. `GET,POST`). Default: all. |
524
+ | `--title <title>` | OpenAPI `info.title`. Default: `Captured consumer contract`. |
525
+ | `--version <ver>` | OpenAPI `info.version`. Default: `0.1.0`. |
526
+ | `--format <fmt>` | Output format: `yaml` (default) or `json`. |
527
+ | `--include-non-json` | Keep entries with non-JSON bodies (default: drop them — schemas can't be inferred). |
528
+
529
+ ### What you get
530
+
531
+ - **Path templating:** concrete paths like `/users/123/orders/abc-2026` are turned into `/users/{userId}/orders/{orderId}` (the param is named from the preceding noun, not a generic `{id}`).
532
+ - **Per-status schema merging:** if two recorded GETs return slightly different shapes, the emitted schema is the merger — fields seen in EVERY sample stay `required`, fields seen in only SOME become optional, integer + number widens to number.
533
+ - **Format detection:** UUIDs, RFC 3339 date-times, and emails get `format: uuid|date-time|email`.
534
+ - **JSON-only by default:** non-JSON bodies are dropped (schemas can't be inferred from binary/HTML); override with `--include-non-json` for debugging.
535
+
536
+ ### Use the captured contract in BDCT
537
+
538
+ ```bash
866
539
  specshield bdct publish-consumer \
867
540
  --org acme-store --consumer checkout-ui \
868
541
  --provider payment-service --version 2.0.0 \
869
- --contract ./contracts/checkout-ui.yaml
542
+ --contract contracts/checkout-ui-payment.yaml \
543
+ --format OPENAPI
544
+ ```
870
545
 
871
- # 3. Gate the provider deployment
872
- specshield bdct can-i-deploy \
873
- --org acme-store --service payment-service \
874
- --version v2.1.0 --env production
546
+ ---
547
+
548
+ # `bdct verify-provider` does your live provider actually match its spec?
549
+
550
+ **Active spec-vs-production conformance — Dredd-style, in CI.**
551
+
552
+ Most teams trust the provider's OpenAPI as the source of truth and never check whether the running service actually matches it. SpecShield's compatibility engine for BDCT is only as accurate as that trust — so `verify-provider` closes the loop by **firing probes derived from the spec at a running endpoint** (staging, usually) and validating every response body against the spec's schema for its status code.
553
+
554
+ Catches things like: the spec says `status ∈ {paid, pending, refunded}` but the live API sometimes returns `partially_refunded`. Or a field documented as `required` is sometimes missing. Or a `format: uuid` field actually contains a numeric ID.
555
+
556
+ ### Safe by default
557
+
558
+ By default `verify-provider` only probes **safe** methods (GET, HEAD, OPTIONS) — it must never side-effect your staging data. Mutating methods are opt-in with `--include-mutating`.
559
+
560
+ ### Run it
561
+
562
+ ```bash
563
+ specshield bdct verify-provider \
564
+ --spec api/openapi.yaml \
565
+ --base-url https://staging.payments.acme.com
566
+ ```
567
+
568
+ ```
569
+ Provider conformance — spec vs https://staging.payments.acme.com
570
+ ────────────────────────────────────────────────────────────
571
+ PASS GET /health (200)
572
+ FAIL GET /payments/{paymentId} (200)
573
+ /amount: must be number
574
+ PASS GET /orders (200)
575
+ SKIP GET /unconfigured/{thingId}
576
+ unresolved path params (no --path-params or spec example): thingId
577
+ ────────────────────────────────────────────────────────────
578
+ 2 pass · 1 fail · 0 error · 1 skip (4 probes)
579
+ ```
580
+
581
+ Exit codes: `0` = all pass · `1` = at least one fail/error · `2` = config/spec error.
582
+
583
+ ### Options
584
+
585
+ | Flag | Purpose |
586
+ |---|---|
587
+ | `--spec <path>` | **Required.** Provider OpenAPI spec (YAML or JSON; `$ref`s are resolved). |
588
+ | `--base-url <url>` | **Required.** Base URL of the running provider (e.g. `https://staging.payments.acme.com`). |
589
+ | `--include-mutating` | Also probe POST/PUT/PATCH/DELETE. **Off by default** — never side-effects staging. |
590
+ | `--path-params <kvList>` | Resolve path params: `paymentId=pay-123,userId=u-7`. Overrides spec examples. Repeatable. |
591
+ | `--header <header>` | Extra request header to send, e.g. `--header "Authorization: Bearer $TOKEN"`. Repeatable. |
592
+ | `--timeout-ms <ms>` | Per-request timeout (default: `8000`). |
593
+ | `--json` | Machine-readable JSON output (for CI parsing). |
594
+
595
+ ### Path-parameter resolution
596
+
597
+ For path templates like `/payments/{paymentId}`, the resolution priority is:
598
+
599
+ 1. `--path-params paymentId=pay-123` on the CLI (always wins)
600
+ 2. The spec's `parameters[].example` for that param
601
+ 3. **SKIPPED** with a reason if neither resolves
602
+
603
+ So you can declare examples in the spec once and never repeat them on the CLI:
604
+
605
+ ```yaml
606
+ paths:
607
+ /payments/{paymentId}:
608
+ get:
609
+ parameters:
610
+ - name: paymentId
611
+ in: path
612
+ required: true
613
+ schema: { type: string }
614
+ example: pay-123 # ← verify-provider uses this automatically
615
+ ```
616
+
617
+ ### What the validator checks (per response)
618
+
619
+ - **Type / format** — `string`/`integer`/`number`/`boolean`/`array`/`object`, plus `format: uuid|date-time|email|...` (via `ajv-formats`).
620
+ - **Required fields** — flags any `required` field that's missing.
621
+ - **Enums** — flags values outside the documented enum set.
622
+ - **`nullable: true`** — properly allows `null` values (OAS-3.0-isms handled).
623
+ - **Status code coverage** — actual status must match an exact code, a wildcard (`4XX`), or the `default` response. An undocumented status is a FAIL.
624
+
625
+ ### Run it from CI
626
+
627
+ ```yaml
628
+ - name: Verify production matches spec
629
+ env:
630
+ STAGING_TOKEN: ${{ secrets.STAGING_TOKEN }}
631
+ run: |
632
+ specshield bdct verify-provider \
633
+ --spec api/openapi.yaml \
634
+ --base-url https://staging.payments.acme.com \
635
+ --header "Authorization: Bearer $STAGING_TOKEN" \
636
+ --json > conformance.json
875
637
  ```
876
638
 
877
639
  ---
878
640
 
879
- ## CI/CD — GitHub Actions
641
+ # CI/CD — GitHub Actions
880
642
 
881
- ### On Pull Request — Catch breaking changes before merge
643
+ ### On Pull Request — Catch breaking spec changes before merge
882
644
 
883
645
  ```yaml
884
646
  name: API Contract Check
885
-
886
647
  on:
887
648
  pull_request:
888
649
  branches: [main]
@@ -892,28 +653,22 @@ jobs:
892
653
  runs-on: ubuntu-latest
893
654
  steps:
894
655
  - uses: actions/checkout@v4
895
- with:
896
- fetch-depth: 0
656
+ with: { fetch-depth: 0 }
897
657
  - uses: actions/setup-node@v4
898
- with:
899
- node-version: '20'
658
+ with: { node-version: '20' }
900
659
  - run: npm install -g specshield
901
- - name: Get base spec
902
- run: git show origin/main:api/openapi.yaml > /tmp/base.yaml
903
- - name: Compare specs
904
- run: specshield compare /tmp/base.yaml api/openapi.yaml --fail-on-breaking
660
+ - run: git show origin/main:api/openapi.yaml > /tmp/base.yaml
661
+ - run: specshield compare /tmp/base.yaml api/openapi.yaml --fail-on-breaking
905
662
  ```
906
663
 
907
- ### On Push — Publish provider spec (BDCT)
664
+ ### On Push — Publish provider spec + gate the deploy
908
665
 
909
666
  ```yaml
910
- name: BDCT Publish Provider Spec
911
-
667
+ name: BDCT Publish & Deploy Gate
912
668
  on:
913
669
  push:
914
670
  branches: [main]
915
- paths:
916
- - 'api/openapi.yaml'
671
+ paths: ['api/openapi.yaml']
917
672
 
918
673
  jobs:
919
674
  publish-bdct:
@@ -921,12 +676,10 @@ jobs:
921
676
  steps:
922
677
  - uses: actions/checkout@v4
923
678
  - uses: actions/setup-node@v4
924
- with:
925
- node-version: '20'
679
+ with: { node-version: '20' }
926
680
  - run: npm install -g specshield
927
681
  - name: Publish provider spec
928
- env:
929
- SPECSHIELD_API_KEY: ${{ secrets.SPECSHIELD_API_KEY }}
682
+ env: { SPECSHIELD_API_KEY: ${{ secrets.SPECSHIELD_API_KEY }} }
930
683
  run: |
931
684
  specshield bdct publish-provider \
932
685
  --org ${{ vars.SPECSHIELD_ORG }} \
@@ -934,9 +687,8 @@ jobs:
934
687
  --version ${{ github.sha }} \
935
688
  --spec ./api/openapi.yaml \
936
689
  --env production
937
- - name: Gate deployment
938
- env:
939
- SPECSHIELD_API_KEY: ${{ secrets.SPECSHIELD_API_KEY }}
690
+ - name: Gate the deploy
691
+ env: { SPECSHIELD_API_KEY: ${{ secrets.SPECSHIELD_API_KEY }} }
940
692
  run: |
941
693
  specshield bdct can-i-deploy \
942
694
  --org ${{ vars.SPECSHIELD_ORG }} \
@@ -945,53 +697,201 @@ jobs:
945
697
  --env production
946
698
  ```
947
699
 
948
- ### On Contract Change Publish consumer contract (BDCT)
700
+ ### NightlySpec-vs-production conformance
949
701
 
950
702
  ```yaml
951
- name: BDCT Publish Consumer Contract
952
-
703
+ name: Provider Conformance
953
704
  on:
954
- push:
955
- branches: [main]
956
- paths:
957
- - 'contracts/bdct/**'
705
+ schedule: [{ cron: '0 7 * * *' }] # daily 07:00 UTC
706
+ workflow_dispatch:
958
707
 
959
708
  jobs:
960
- publish-consumer:
709
+ conformance:
961
710
  runs-on: ubuntu-latest
962
711
  steps:
963
712
  - uses: actions/checkout@v4
964
713
  - uses: actions/setup-node@v4
965
- with:
966
- node-version: '20'
714
+ with: { node-version: '20' }
967
715
  - run: npm install -g specshield
968
- - name: Publish consumer contract
969
- env:
970
- SPECSHIELD_API_KEY: ${{ secrets.SPECSHIELD_API_KEY }}
716
+ - name: Verify production matches its spec
717
+ env: { STAGING_TOKEN: ${{ secrets.STAGING_TOKEN }} }
971
718
  run: |
972
- specshield bdct publish-consumer \
973
- --org ${{ vars.SPECSHIELD_ORG }} \
974
- --consumer checkout-ui \
975
- --provider payment-service \
976
- --version ${{ github.ref_name }} \
977
- --contract ./contracts/bdct/checkout-ui-payment.yaml
719
+ specshield bdct verify-provider \
720
+ --spec api/openapi.yaml \
721
+ --base-url https://staging.payments.acme.com \
722
+ --header "Authorization: Bearer $STAGING_TOKEN"
978
723
  ```
979
724
 
980
725
  ---
981
726
 
982
- ## Config File
727
+ ## vs. Alternatives
728
+
729
+ | | **SpecShield** | **Pact / PactFlow** (SmartBear) | **oasdiff** | **Specmatic** / **Microcks** (OSS) |
730
+ |---|---|---|---|---|
731
+ | OpenAPI-native | ✅ | partial (Pact DSL) | ✅ | ✅ |
732
+ | No broker required | ✅ | ❌ (Pact Broker) | ✅ | ✅ |
733
+ | Bi-directional contract testing | ✅ | ✅ (Pactflow paid) | ❌ | partial |
734
+ | Breaking-change detection | ✅ | ❌ | ✅ | ❌ |
735
+ | `can-i-deploy` gating | ✅ | ✅ (broker) | ❌ | ❌ |
736
+ | **Pact JSON ingest** | ✅ | native | ❌ | ❌ |
737
+ | **HAR → consumer contract capture** | ✅ | ❌ (requires Pact DSL in tests) | ❌ | ❌ |
738
+ | **Spec-vs-production conformance** | ✅ | partial (ReadyAPI / Dredd) | ❌ | ✅ |
739
+ | GitHub App PR checks | ✅ | ❌ | ❌ | ❌ |
740
+ | Hosted dashboard | ✅ | ✅ (paid) | partial | ❌ |
741
+ | CLI-first | ✅ | ❌ | ✅ | partial |
742
+ | Free tier | ✅ | ❌ | ✅ (OSS) | ✅ (OSS) |
743
+
744
+ ---
745
+
746
+ ## Pricing
747
+
748
+ | Plan | Price | What's included |
749
+ |---|---|---|
750
+ | **Free** | $0 forever | Local compare · `bdct capture from-har` · `bdct verify-provider` · public web diff · 7-day compare history · 1 GitHub App PR check |
751
+ | **Team** | $89/mo ($75 annual) | Everything in Free + consumer registry · enforced `can-i-deploy` gate · audit trail · BDCT compatibility matrix · Slack alerts · team workspace + RBAC · priority email support |
752
+ | **Enterprise** | Talk to us | Everything in Team + SAML SSO · SCIM · on-prem / private-cloud · advanced RBAC + audit-log export · dedicated onboarding + SLA |
753
+
754
+ No credit card for the free plan. **[Get started free →](https://specshield.io)** · For Enterprise: [sales@specshield.io](mailto:sales@specshield.io).
755
+
756
+ ---
757
+
758
+ ## `specshield init` — first-run setup wizard
759
+
760
+ Run once at the root of your project. The wizard:
761
+
762
+ 1. Detects your OpenAPI spec (looks under `api/`, `spec/`, `docs/`, repo root).
763
+ 2. Detects your service name (`package.json`, `pyproject.toml`, `pom.xml`, `go.mod`, `Cargo.toml`, or directory name).
764
+ 3. Detects your git branch and suggests `production` for `main`/`master`, `staging` otherwise.
765
+ 4. Asks whether this project is a provider, a consumer, or both.
766
+ 5. Asks for your org key (autocompletes from your account if signed in).
767
+ 6. Validates / stores your API key.
768
+ 7. Writes **`.specshield.yml`** and (optionally) a starter **`.github/workflows/specshield-bdct.yml`** using [`specshield26/bdct-action@v1`](https://github.com/marketplace/actions/specshield-bdct).
769
+
770
+ ```bash
771
+ specshield init
772
+ ```
773
+
774
+ ### Example `.specshield.yml`
775
+
776
+ ```yaml
777
+ schemaVersion: 1
778
+
779
+ failOnBreaking: true
780
+ severity: error
781
+
782
+ bdct:
783
+ org: acme-pay
784
+ environment: staging
785
+
786
+ provider:
787
+ name: payment-service
788
+ spec: api/openapi.yaml
789
+
790
+ # consumer (optional — present when --kind=consumer or --kind=both):
791
+ # consumer:
792
+ # name: checkout-ui
793
+ # provider: payment-service
794
+ # contract: contracts/payment-service.yaml
795
+ # format: OPENAPI
796
+
797
+ github:
798
+ specPath: api/openapi.yaml
799
+ failOnBreaking: true
800
+ commentOnPr: true
801
+ ```
802
+
803
+ CLI flags **always** override this file. Paths in the file are resolved relative to the file's own directory, so you can run `specshield bdct …` from any subdirectory.
804
+
805
+ ### Non-interactive (scriptable) mode
806
+
807
+ ```bash
808
+ # Provider-only project
809
+ specshield init --no-interactive \
810
+ --kind provider --org acme-pay \
811
+ --provider payment-service --spec api/openapi.yaml \
812
+ --env staging --write-workflow
983
813
 
984
- Run [`specshield init`](#specshield-init--first-run-setup-wizard) to generate
985
- `.specshield.yml` automatically, or hand-write it. Either way, every
986
- `specshield ...` invocation reads the file from the project root (or any
987
- parent directory) and uses its values as defaults — CLI flags always win.
814
+ # Consumer-only project
815
+ specshield init --no-interactive \
816
+ --kind consumer --org acme-pay \
817
+ --consumer checkout-ui --consumer-provider payment-service \
818
+ --contract contracts/payment-service.yaml --format OPENAPI \
819
+ --env staging
820
+ ```
821
+
822
+ ### All `init` flags
823
+
824
+ | Flag | Purpose |
825
+ |---|---|
826
+ | `--no-interactive` | Run without prompts; required fields must come from flags or auto-detection. |
827
+ | `--print` | Detect everything, print the proposed YAML to stdout, write nothing. |
828
+ | `--force` | Skip the overwrite-confirmation if `.specshield.yml` already exists. |
829
+ | `--server <url>` | Use a non-default SpecShield endpoint (self-hosted / staging). |
830
+ | `--kind <kind>` | `provider`, `consumer`, `both`, or `skip`. |
831
+ | `--org <key>` | Organization key. |
832
+ | `--provider <name>` | Provider service name. |
833
+ | `--spec <path>` | Path to the provider OpenAPI spec. |
834
+ | `--consumer <name>` | Consumer service name. |
835
+ | `--consumer-provider <name>` | The provider this consumer talks to. |
836
+ | `--contract <path>` | Path to the consumer contract (OpenAPI or Pact JSON). |
837
+ | `--format <fmt>` | Consumer contract format: `OPENAPI` (default) or `PACT`. |
838
+ | `--env <environment>` | Default environment for BDCT operations. |
839
+ | `--write-workflow` | Also write a starter GitHub Actions workflow. |
840
+
841
+ > **What is _not_ written into `.specshield.yml`:** your API key. It's stored in `~/.specshield/config.json` (set by `specshield login`) or read from `SPECSHIELD_API_KEY` in CI. The project file is committed; never commit a secret into it.
842
+
843
+ ---
844
+
845
+ ## Login & Authentication
846
+
847
+ **Step 1 — Create your account**
848
+
849
+ Go to [https://specshield.io](https://specshield.io) and sign in with GitHub or Google (30 seconds).
850
+
851
+ **Step 2 — Generate an API key**
852
+
853
+ Dashboard → **API Keys** → **Generate Key** → copy the `ss_` token.
854
+
855
+ **Step 3 — Authenticate the CLI**
988
856
 
989
- The full schema supports both local-compare defaults and BDCT defaults:
857
+ ```bash
858
+ specshield login --api-key ss_your_token_here
859
+ ```
860
+
861
+ ```
862
+ ✔ Logged in successfully.
863
+ Customer : Your Name
864
+ Plan : FREE
865
+ ```
866
+
867
+ The token is stored in `~/.specshield/config.json` — no need to pass it on every command.
868
+
869
+ **Or use an environment variable (recommended for CI):**
870
+
871
+ ```bash
872
+ export SPECSHIELD_API_KEY=ss_your_token_here
873
+ ```
874
+
875
+ This one env var works for every command — `compare`, `login`, and every `bdct` subcommand all read it.
876
+
877
+ **Token resolution order:**
878
+ - `compare` / `login`: `--api-key` flag → `SPECSHIELD_API_KEY` env var → stored config → `.specshield.yml`
879
+ - `bdct …` subcommands: `--api-token` flag → `SPECSHIELD_API_KEY` env var → stored config
880
+
881
+ > **Why two flag names?** `compare` and `login` were built first and used `--api-key`. The newer `bdct` commands use `--api-token` to keep "key" reserved for the stored-config concept. The env var unifies both.
882
+
883
+ > **`bdct capture from-har` and `bdct verify-provider` don't require auth** — they run entirely locally against files / your own staging URL, and nothing is uploaded.
884
+
885
+ ---
886
+
887
+ ## Config File
888
+
889
+ Run [`specshield init`](#specshield-init--first-run-setup-wizard) to generate `.specshield.yml`, or hand-write it. Every `specshield …` invocation reads it from the project root (or any parent directory) and uses its values as defaults — CLI flags always win.
990
890
 
991
891
  ```yaml
992
892
  schemaVersion: 1
993
893
 
994
- # Local-compare defaults (used by `specshield compare`).
894
+ # Local-compare defaults.
995
895
  failOnBreaking: true
996
896
  severity: error # info | warning | error
997
897
  ignore:
@@ -1002,14 +902,12 @@ remote:
1002
902
  enabled: false
1003
903
  url: https://specshield.io/compare
1004
904
  timeout: 10000
1005
- # apiKey is intentionally NOT set here. Use SPECSHIELD_API_KEY in CI
1006
- # or `specshield login` locally. Never commit a key to a file.
1007
905
 
1008
- # BDCT defaults (used by every `specshield bdct ...` subcommand).
906
+ # BDCT defaults (used by every `specshield bdct …` subcommand).
1009
907
  bdct:
1010
908
  org: acme-pay
1011
909
  environment: staging
1012
- # server: https://specshield.io # only set for self-hosted / staging
910
+ # server: https://specshield.io # only for self-hosted / staging
1013
911
 
1014
912
  provider:
1015
913
  name: payment-service
@@ -1029,7 +927,7 @@ github:
1029
927
  commentOnPr: true
1030
928
  ```
1031
929
 
1032
- When this file is present, BDCT commands collapse to just `--version`:
930
+ With this file, BDCT commands collapse to `--version`:
1033
931
 
1034
932
  ```bash
1035
933
  specshield bdct publish-provider --version $GITHUB_SHA
@@ -1090,6 +988,8 @@ specshield bdct <subcommand> [options]
1090
988
  | `list-providers` | List published provider specs |
1091
989
  | `list-consumers` | List published consumer contracts |
1092
990
  | `list` | List verification history |
991
+ | **`capture from-har`** | **Turn a recorded HAR file into an OpenAPI consumer contract** |
992
+ | **`verify-provider`** | **Check that a running provider service matches its OpenAPI spec** |
1093
993
 
1094
994
  Every `bdct` subcommand accepts these flags in common:
1095
995
 
@@ -1110,8 +1010,10 @@ Per-subcommand flags:
1110
1010
  | `can-i-deploy` | `--service <name>`, `--version <ver>`, `--env <env>` |
1111
1011
  | `matrix` | `--env <env>` |
1112
1012
  | `list-providers` | `--provider <name>` (filter) |
1113
- | `list-consumers` | `--consumer <name>` (filter), `--provider <name>` (filter) |
1013
+ | `list-consumers` | `--consumer <name>`, `--provider <name>` (filters) |
1114
1014
  | `list` | `--consumer <name>`, `--provider <name>`, `--env <env>`, `--page <n>`, `--size <n>` |
1015
+ | **`capture from-har`** | `--in <path>` *required*, `--out <path>`, `--base-url <url>`, `--method <verbs>`, `--title <s>`, `--version <s>`, `--format yaml\|json`, `--include-non-json` |
1016
+ | **`verify-provider`** | `--spec <path>` *required*, `--base-url <url>` *required*, `--include-mutating`, `--path-params <kv>` (repeatable), `--header <h>` (repeatable), `--timeout-ms <n>`, `--json` |
1115
1017
 
1116
1018
  ---
1117
1019
 
@@ -1119,8 +1021,8 @@ Per-subcommand flags:
1119
1021
 
1120
1022
  | Code | Meaning |
1121
1023
  |---|---|
1122
- | `0` | Clean — no breaking changes / deployable |
1123
- | `1` | Breaking changes found / not deployable |
1024
+ | `0` | Clean — no breaking changes / deployable / all probes pass |
1025
+ | `1` | Breaking changes found / not deployable / one or more probes failed |
1124
1026
  | `2` | Config error, missing token, or runtime error |
1125
1027
 
1126
1028
  ---
@@ -1133,8 +1035,6 @@ MIT © Deepak Satyam
1133
1035
 
1134
1036
  ## Support
1135
1037
 
1136
- Questions or issues?
1137
-
1138
1038
  - 📧 [admin@specshield.io](mailto:admin@specshield.io)
1139
1039
  - 🐛 [Open an issue on GitHub](https://github.com/specshield26/specshield-cli/issues)
1140
1040
  - 🌐 [specshield.io](https://specshield.io)