dánwòdánwò

Real stacks

Outside hosts

Decide what happens to calls no emulator answers, keep analytics out of your runs, and catch live keys before they leak.

danwo emulates the providers you list. Your app may call other hosts too: a search service, a logo API, an analytics tracker. You decide what happens to those calls, so a test run never sends real data out by accident.

Pass through or block

unmatched sets what happens to hosts no service answers:

unmatchedCalls to unemulated hostsUse it when
passthrough (default)go to the real hostyou're starting out
blockget a 502 from the proxyyou want the run sealed off
danwo.config.ts
import { appEnvironment, defineConfig, stripeService } from "@danwo/sdk"

export default defineConfig({
  testDir: "tests",
  environment: () =>
    appEnvironment({
      app: { command: "npm run dev", url: "http://localhost:3000" },
      services: [stripeService()],
      unmatched: "block",
      allow: ["fonts.googleapis.com"],
    }),
})

Start with block. A 502 in the flight record names every host your app needs, so you know what to emulate, allow or start locally.

What still passes under block

Blocking stops your app's calls to the real world. It doesn't stop the page from showing a logo from a CDN. Under block, these still go through:

CallUnder block
to this machine (localhost, 127.0.0.1, ::1)passes
to a host in allowpasses
a page's image, font, stylesheet, video, audio or trackpasses, recorded as an asset
a page's script, frame, worker, or fetch()502
a server's own call, or any call that isn't a GET502
to a telemetry host or a host in blocked502, even for an image

The browser says what each request loads, so danwo can tell a logo from a data call to the same host. A script is never let through: it runs code that calls on.

Missing images or fonts in a recording

Images and fonts go missing when you set assets: "block", which refuses page assets too, or when the page loads them with fetch(). Either way, the run names the hosts once at the end:

Blocked hosts the pages tried to load: cdn.sanity.io (19 images), api.example.com (2 requests).
Add the ones the app needs to `allow`, or set `assets: "passthrough"` for images and fonts.

The same line is a note on the attempt's flight record, so it shows in the dashboard too. Allow the hosts the page needs, or go back to the default:

danwo.config.ts (fragment)
appEnvironment({
  app: { command: "npm run dev", url: "http://localhost:3000" },
  unmatched: "block",
  assets: "passthrough",
  allow: ["cdn.sanity.io"],
})

This machine always passes

Calls to this machine (localhost, 127.0.0.1, ::1) always go through, even when other hosts are blocked. That's where your other apps, the Inngest dev server and your databases answer.

If nothing listens on the port, the call gets a 502 that says so:

nothing is listening on 127.0.0.1:8108; start that service or route the host

That usually means a service your app needs, such as a search server, isn't running.

Allow and block by name

OptionWhat it does
allowhosts that always pass, even under block
blockedhosts that always get 502, even under passthrough; beats allow and this machine
telemetryblock (default) or passthrough for analytics hosts
assetspassthrough (default) or block for a page's images, fonts and media under block

Analytics and telemetry stay out

PostHog, Sentry, Segment, Amplitude, Datadog, hosted trace backends, Google Analytics and the like are blocked by default. A test run sends them no real data, and your flight records stay free of their noise. Set telemetry: "passthrough" to let them through.

Live keys are caught

With hosts passing through, a live-looking key for a provider the run doesn't emulate would reach the real provider. When a stack starts, danwo looks for them:

  • sk_live_…, OpenAI and Anthropic keys, AWS access keys and similar;
  • in an app's environment, or a .env file in its folder that the run doesn't override.

Each one is a warning naming the variable and the file, never the value.

secrets (stack option)Live-looking key found
warn (default)a warning per key
failthe environment refuses to start
ignorenothing

Next

On this page

Run it on every pull request

A GitHub check and one comment per pull request, with the flight records attached.

Set up CI