Skip to content

dhanesh/mockstar

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

65 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mockstar

quickstart-smoke release

Status: pre-1.0. Minors may break mocks-file shape — see docs/VERSIONING.md. Pin $schema to https://schemas.mockstar.dev/v0.<N>/mock.json for stability.

A Bun-based mock server with static + dynamic mocking, JSON config, named JS handlers, pass-through routing, multi-tenancy, and test-data utilities.

Demo

mockstar demo — make docker-run + curl

Recorded with asciinema — raw cast at docs/media/demo.cast. Regenerate with make record-demo (requires asciinema, jq, npx, Docker).

For SDETs

Mockstar ships a library embed (import { launch } from '@dhanesh/mockstar') supported across Jest 30, Jest 29, Vitest, and bun test. See docs/SDET.md

Contributing

Read CONTRIBUTING.md. Maintainers: docs/TEAM-WORKFLOW.md.

Mockstar targets three personas equally:

  • SDETs — library-embed in test suites, ephemeral instances per CI run, deterministic mode
  • Developersbunx @dhanesh/mockstar ./mocks with file-watch hot reload
  • DevOps — Docker image in shared staging with per-tenant ConfigMap mounts

Built on Hono on Bun. Targets p99 < 5ms for static mock responses. See DECISIONS.md for the constraint-first design record.

Quick start

bun install
bun run src/cli.ts ./examples/mocks
# http://localhost:3000

A persona-specific deployment walkthrough lives in docs/DEPLOYMENT.md.

Config layout

mocks/
  default/              # one directory per tenant
    users.json          # a mock config file
    orders.json
handlers/               # named JS function handlers
  echo.ts

See docs/CONFIG.md for the full schema and docs/HANDLERS.md for dynamic handlers.

Response templating

Any string value inside response.body or response.headers can contain {{ … }} placeholders. Whole-string placeholders in a JSON body preserve their source type — a number stays a number, an object stays an object.

Request reflection

Token Value
{{request.method}} HTTP verb (GET, POST, …)
{{request.path}} Full request path
{{request.params.<name>}} Path parameter captured by :name in match.path
{{request.query.<name>}} URL query-string value
{{request.headers.<name>}} Request header value (case-insensitive)
{{request.body.<dot.path>}} Dot-path into the parsed JSON request body

Context

Token Value
{{tenant}} Tenant identifier the request was routed to
{{requestId}} Per-request UUID assigned by mockstar

Random data (faker)

Token Value
{{faker.uuid}} Random UUID v4
{{faker.email}} Random email address
{{faker.name}} Random full name
{{faker.integer(min, max)}} Random integer in [min, max]
{{faker.pick(["a","b","c"])}} Random element from the array
{{faker.boolean}} true or false
{{faker.dateIso}} Random recent date as ISO 8601 string

Provider-shape IDs

Token Value
{{id("prefix_", 14)}} prefix_ + 14 random base62 chars — e.g. order_4OwxzMjhPIt4YQ
{{id("prefix_", 14, "0123456789abcdef")}} Same with a custom alphabet (hex shown)
{{id.named("key", "prefix_", 14)}} Mint once per request per name — repeated calls with the same key return the identical value, useful when the same ID appears in multiple fields

Timestamps

Token Value
{{now.unix}} Current time as Unix seconds (number)
{{now.millis}} Current time as Unix milliseconds (number)
{{now.iso}} Current time as ISO 8601 string

In --deterministic mode (MOCKSTAR_DETERMINISTIC=1) all faker and now.* tokens return fixed seed-derived values so CI replays are byte-identical.

Full reference including type-preservation rules and worked examples: docs/TIER2.md.

Outbound webhooks

Attach webhooks: [...] to any mock entry to fire HTTP deliveries after the matched response flushes — useful for verifying receiver-side webhook handlers in integration tests, dogfooding partner integrations, and modeling provider behavior in fixtures.

{
  "id": "orders-create",
  "match": { "method": "POST", "path": "/orders" },
  "response": { "kind": "static", "status": 201, "body": { "id": "{{id(\"ord_\", 12)}}" } },
  "webhooks": [{
    "id": "order-created",
    "url": "https://api.partner.example/hooks/order-created",
    "method": "POST",
    "headers": { "content-type": "application/json" },
    "body": { "orderId": "{{request.body.orderId}}", "tenant": "{{tenant}}" }
  }]
}

Capabilities at a glance:

Configuration channels Per-route url, {{ env.NAME }} interpolation, admin API, opt-in X-Mockstar-Webhook-Url request header (gated by --allow-webhook-url-header)
Delivery contract At-least-once within queue + circuit bounds, exponential backoff with jitter (default [1s, 2s, 4s, 8s, 16s]), idempotent X-Mockstar-Delivery-Id header
Signing Opt-in HMAC-SHA256, Stripe-style ${ts}.${rawBody} payload, secrets via {{ env.X }} or file:/path (inline rejected at config-load)
Reliability Per-webhook circuit breaker, drop-oldest queue cap, per-attempt AbortSignal.timeout, optional expectResponse: { status, body } body assertion
Observability /__admin/tenants/:t/webhooks list (secrets redacted), /webhooks/journal history, POST /webhooks/await?id=… for sync test assertions, POST /webhooks/:id/replay for recovery
Metrics webhook_delivery_total{outcome}, webhook_delivery_latency_us, webhook_queue_depth, webhook_queue_dropped_total, webhook_circuit_state
Distribution In-process (no Redis); single-binary and SDET-embed friendly

Full guide, decisions log, and security model: docs/webhooks/README.md, docs/webhooks/DECISIONS.md, docs/webhooks/SECURITY.md. Worked example loadable via bun run dev: examples/mocks/default/webhooks-example.json.

What's in v1, what's deferred

See CHANGELOG.md. TL;DR: static + dynamic + pass-through + scenarios + outbound webhooks + OpenAPI import + admin read endpoints + multi-tenancy ship in v1. Stateful mocks, GraphQL, gRPC, fault injection, and a config-mutation admin API are explicitly deferred to v1.1.

HTTPS transparent upstream (mockstar proxy)

Point real HTTPS traffic at your local mocks with zero application code changes — https://api.razorpay.com resolves to 127.0.0.1, terminates on a mkcert-trusted leaf, and forwards to mockstar-on-3000. macOS + Linux only in v1.

mockstar proxy install           # one-time: installs CA, DNS, port-bind grant
mockstar proxy start             # runs the HTTPS listener on :443

See docs/PROXY.md for the full guide and docs/PROXY-RECOVERY.md for incident recovery.

Running with Docker

The image is published multi-arch (linux/amd64 + linux/arm64), public, and signed:

docker pull ghcr.io/dhanesh/mockstar:latest        # or a pinned tag, e.g. :v0.1.0-alpha.1

The image is lean — it ships no mocks. You mount your mock content at run time. The default command serves /config/mocks; named JS handlers (if any) are read from /config/handlers. It listens on port 3000 and runs as a non-root user (uid 10001).

Run with your mocks mounted

Point a host folder at /config/mocks (and /config/handlers if you use named handlers):

docker run --rm -p 3000:3000 \
  -v "$PWD/mocks:/config/mocks:ro" \
  -v "$PWD/handlers:/config/handlers:ro" \
  ghcr.io/dhanesh/mockstar:latest

# in another shell:
curl localhost:3000/health        # {"status":"ok"}
curl localhost:3000/users/123     # your mocked response

Hardened run (read-only root filesystem + writable temp), matching the Helm chart's security posture:

docker run --rm -p 3000:3000 \
  --read-only --tmpfs /tmp \
  -v "$PWD/mocks:/config/mocks:ro" \
  ghcr.io/dhanesh/mockstar:latest

Adding more mocks

The mounted folder follows the Config layoutone sub-directory per tenant, one JSON file per resource:

mocks/
  default/            # the default tenant
    users.json
    orders.json
    billing.json      # ← drop in new files to add more routes
  acme/               # add a directory to add a tenant
    users.json
handlers/             # optional named JS/TS handlers referenced by dynamic mocks
  computeTotal.ts

Add a file (or tenant directory) under your mounted mocks/ folder and it's served — no image rebuild. Mock JSON changes are hot-reloaded on the mounted volume; named handler changes require a container restart (handlers are loaded at boot).

Bake mocks into an image (shared deployments)

For a self-contained, versioned fixture set your team/CI can pull and run with no mount, build a thin image on top of the published base:

# Dockerfile
FROM ghcr.io/dhanesh/mockstar:latest
# Copy your fixtures in (owned by the non-root runtime user)
COPY --chown=10001:10001 mocks/     /config/mocks/
COPY --chown=10001:10001 handlers/  /config/handlers/   # omit if you have no handlers
# Base image already sets ENTRYPOINT + CMD (serve /config/mocks on :3000)
docker build -t ghcr.io/your-org/team-fixtures:1.0 .
docker run --rm -p 3000:3000 ghcr.io/your-org/team-fixtures:1.0   # mocks are baked in

Pin the base by digest (FROM ghcr.io/dhanesh/mockstar@sha256:…) for reproducible builds, push the result to your own registry, and every consumer gets the identical mock set.

Kubernetes / Helm

Deploy the mock + webhook server with the chart in charts/mockstar — secure-by-default (read-only root FS, non-root uid 10001, dropped caps, admin token via Secret).

helm install mockstar oci://ghcr.io/dhanesh/charts/mockstar \
  --version 0.1.0 --namespace mockstar --create-namespace
  • Single replica by design (replicaCount: 1, Recreate): the journal, scenario state, and webhook queue are in-memory and per-pod — scaling beyond 1 splits that state.
  • The TLS-intercepting mockstar proxy is a local-only experimental dev tool and is not deployed by the chart.

Full guide — tenant ConfigMaps, handlers, admin token Secret, NetworkPolicy egress, probes/metrics, image-by-digest: docs/deployment/helm.md.

Security

See docs/SECURITY.md for the threat model, SECURITY.md for the security policy and posture. Admin endpoints are disabled by default. Pass-through and OpenAPI import share a hardened URL validator that rejects private-range targets by default (addressing CVE-2026-39885-class OpenAPI $ref attacks).

License & governance

MIT — see LICENSE. Governance policy, irreversible-decision watch list, and the protocol for changing project-level commitments live in docs/GOVERNANCE.md.

About

No description, website, or topics provided.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages