The CLI is out. The platform is not.

Mocking that movesas fast as your code

A single Rust binary that stands in for any HTTP API, and tells you when your mocks drift from it.

curl -fsSL https://apifae.com/install.sh | sh 

Trouble installing? See the manual.

Drift check~/acme-api
$ apifae diff https://api.acme.dev
GET /v1/invoices
Drift detected:
~ status: 200 → 201
~ $.data[0].line_items[0].unit_amount: Number → String
+ $.data[0].line_items[0].tax_rate (Number)
GET /v1/customers
No drift
Summary: 12 checked, 1 drifted, 11 unchanged

A mock is only true on the day you write it.

The API it stands in for keeps moving. Your suite keeps passing. Nothing in the run tells you the two stopped agreeing three weeks ago.

apifae diff https://api.acme.dev Drift detected
mocks/invoices.yamlrecorded · 200
{  "data": [    {      "id": "inv_8f21c3",      "line_items": [        {          "sku": "ACME-42",          "unit_amount": 4200        }      ]    }  ]}
GET /v1/invoiceslive · 201
{  "data": [    {      "id": "inv_8f21c3",      "line_items": [        {          "sku": "ACME-42",          "unit_amount": "42.00",          "tax_rate": 0.2        }      ]    }  ]}
status: 200 → 201$.data[0].line_items[0].unit_amount: Number → String$.data[0].line_items[0].tax_rate (Number)Summary: 12 checked, 1 drifted, 11 unchanged
That is the whole product. apifae diff compares every recorded mock against the live API and reports what moved; apifae patch applies it, dry run first, one change at a time.

APIFae is one native binary that checks its own mocks against the real thing.

You already have a mock server. Here is what this changes.

  1. A runtime you did not ask for

    WireMock runs on the JVM. Prism installs as a global npm package and needs Node. Mockoon is a desktop application whose CLI ships as a separate npm package. Whichever you pick, something has to be installed and patched for as long as the project lives, on your machine and on every CI runner. APIFae is one native binary.

  2. It lives in your repository, not in a server

    A mock somebody wrote by hand sits on their laptop and nowhere else. Recorded traffic and named scenarios are the same story: WireMock keeps them as stub mappings inside a running server, poked over an admin API. An APIFae workspace is plain files — mocks, scenarios, config — so capturing an API is a diff you read, and changing a scenario is a commit a teammate reviews.

  3. Something has to check the mock

    A spec checker tells you an implementation has wandered from its document. That is a different question from whether the stand-in you are testing against still resembles the API it stands in for, and the second one is what silently rots. apifae diff asks it on demand and exits non-zero, so the pipeline stops instead of printing a report nobody reads.

Four commands, and what they print.

Every block below is the binary’s own output. One of them carries the argument and gets the room; the rest are quoted in excerpt.

Available nowEleven commands ship. These four are the loop: capture it, compare it, apply it, serve it.

apifae diff https://api.acme.dev

Ask the live API whether your mocks are still telling the truth. It reads the workspace that record fills, and patch consumes what it reports.

Comparing against https://api.acme.dev

GET /v1/customers
 No drift

GET /v1/invoices
 Drift detected:
    ~ status: 200 → 201
    ~ $.data[0].line_items[0].unit_amount: Number → String
    + $.data[0].line_items[0].tax_rate (Number)

Summary: 12 checked, 1 drifted, 11 unchanged
  1. no drift

    This endpoint still answers exactly as the mock says it does. That line is the whole point of running this in the morning.

  2. drift detected

    This one does not. The status changed, a field changed type underneath it, and a new field appeared. Named, not just counted.

  3. summary

    Eleven of the twelve endpoints checked still match api.acme.dev. Drift exits non-zero, so this is a gate in CI rather than a report somebody has to remember to read. --json prints the same run for a machine.

  1. apifae recordhttps://api.acme.dev

    Sit between your app and the real API, and keep what it answers.

     Recording  https://api.acme.dev (proxy on :4001)
      [14:02:21] POST   /v1/orders                     201 (137ms, 640b)
      3 requests captured | Press Ctrl+C to stop
    
  2. apifae patchhttps://api.acme.dev --dry-run

    Fold the drift back into the mock files. Preview it first.

    GET /v1/invoices
      ~ status → 201
      ~ $.data[0].line_items[0].unit_amount → "42.00"
      ~ $.data[0].line_items[0].tax_rate → 0.2
    Would update mocks/invoices.yaml
    Summary: 1 files would be updated (dry-run, no changes made)
    
  3. apifae up--watch --scenario checkout-empty

    Serve the workspace, and reload it the moment a file changes.

     APIFae mock server running at http://127.0.0.1:4000
      Loaded 24 endpoints from 6 files
     Reloaded (24 endpoints) — mocks/orders.yaml changed
    
  • apifae init
  • apifae serve
  • apifae validate
  • apifae status
  • apifae scenario
  • apifae config
  • apifae ca

Also shipping, in one Rust binary with no runtime behind it. Install it from any of 4 channels.

The CLI shipped. Here is the rest.

One release is out. None of it runs yet beyond that, and there are no dates — this is the order, not a schedule.

  1. Shipped

    1. The CLI v0.1.0

      Eleven commands ship, on four install channels.

  2. Next

    1. Cloud

      Shared workspaces: push one, your team pulls it.

    2. Dashboard

      Visual state machine, diff on screen, chaos controls.

  3. Later

    1. Sidecar

      Passive drift alerts from staging traffic.

    2. Self-host

      The platform in your own network, with SSO, roles and audit logs.

Stop trusting a mock that stopped telling the truth.

The CLI is out and you can install it today. Leave an address, confirm it from the email we send, and we’ll tell you when the platform lands.