Docs/Overview

Kifas, in three primitives.

Kifas is a testing platform built for the era when most tests are written, run, and debugged by AI agents. The whole API stands on three primitives — workflows, state handles, and devices — and everything else (visual regression, tunnels, MCP, CI) composes from there.

Why does this exist?

Existing test infra was designed for QA engineers writing Selenium scripts in 2014. It bills per browser-minute, hides the network from your AI assistant, and treats authentication as a problem you solve once a year. We bet that the next decade looks different — agents will draft most tests, run them on cloud emulators, and read back results without humans gating every check.

So we built Kifas to be agent-native from day one: every primitive is reachable from the SDK, the MCP server, and the visual workflow editor with the same exact semantics. What your agent does, your CI does, what your designer drags onto a canvas does — all the same call.

The shape of a test run

// 12 lines. Reuses warm Chromium, real Gmail login, AI step.
import { test, expect } from '@kifas/playwright';

test('gmail compose', async ({ kifas, page }) => {
  await kifas.browser.spawn('chromium-131');
  await kifas.auth.loginAs('gmail', { user: 'qa@acme.dev' });
  await kifas.state.save('admin_logged_in');

  await page.goto('https://gmail.com/inbox');
  await kifas.ai.step('Compose new email to raj@acme.dev');

  await expect(page.getByText('Welcome, Maya')).toBeVisible();
});
Tip. The same test runs on a Pixel 9 emulator by swapping browser.spawn for device.spawn('pixel-9'). No new APIs.

What's next

  • Quickstart — get a green run in under 3 minutes.
  • Workflows — the canvas + JSON model behind every test.
  • MCP — wire your Claude Code / Cursor / Windsurf agent into Kifas.
Docs/Quickstart

Quickstart v1.4.2

From zero to a green test run in under three minutes. We'll spin up a real Chromium, log into a real Gmail account, and assert against the inbox — all on the free tier.

1. Install

$ npm install @kifas/sdk @kifas/playwright
$ npx @kifas/cli login

The CLI opens a browser tab to authenticate and writes a token to ~/.kifas/config. No keys to copy-paste.

2. Add your first auth handle

Kifas's Auth Catalog is a managed vault of pre-built login flows for Gmail, Google Workspace, Microsoft, GitHub, Okta, Auth0, Stripe, and 42 others. Add one once, reuse it across every test.

$ npx @kifas/cli auth add gmail \
    --user qa@acme.dev \
    --totp-secret $KIFAS_TOTP_SECRET

→ Verified. Catalog handle: gmail:qa@acme.dev

3. Write a 12-line test

// tests/inbox.spec.ts
import { test, expect } from '@kifas/playwright';

test('inbox loads', async ({ kifas, page }) => {
  await kifas.browser.spawn('chromium-131');
  await kifas.auth.loginAs('gmail:qa@acme.dev');
  await page.goto('https://gmail.com/inbox');
  await expect(page.getByRole('main')).toBeVisible();
});

4. Run it

$ npx playwright test
→ inbox loads … passed (3.1s)
→ Trace: https://app.kifas.io/runs/r_a8f29
Reading the trace — every run gets a deep link with the full DOM tree, network log, console, and AI rationale (if you used ai.step). Click any failed step to replay it from that exact frame.

5. Where to go next

Docs/Install the SDK

Install the SDK

Kifas ships TypeScript-first. Python and Go SDKs cover the same surface area; bindings are auto-generated from the same OpenAPI spec the MCP server publishes.

TypeScript / JavaScript

$ npm install @kifas/sdk
# Or for Playwright fixtures:
$ npm install @kifas/playwright

Python

$ pip install kifas-sdk
from kifas import Kifas

kifas = Kifas()
browser = kifas.browser.spawn('chromium-131')
kifas.auth.login_as('gmail:qa@acme.dev')

Go

$ go get github.com/kifas/kifas-go

Authentication

The SDK reads KIFAS_API_KEY from the environment, or a token stored at ~/.kifas/config (created by npx @kifas/cli login). Prefer the CLI on workstations and the env var in CI.

VariableRequired?Description
KIFAS_API_KEYYes (CI)Project-scoped API key. Rotate from Settings → API.
KIFAS_PROJECTOptionalOverride the project the key resolves to.
KIFAS_REGIONOptionalus-east-1 (default), eu-west-1, ap-south-1.
KIFAS_TUNNELOptionalAuto-attach Kifas Tunnel for localhost testing.
Docs/Workflows

Workflows

A workflow is a directed graph of nodes connected by edges. Each node is a typed step (action, auth, assert, AI, hook, sub-workflow). The same workflow can be authored in code, dragged on the canvas, or written by an MCP-connected agent — all three produce the same JSON document.

Node types

TypeStripePurpose
actionBrowser actions: navigate, click, type, scroll.
authLogin via Auth Catalog. Returns a state handle.
stateSave / load named browser state (cookies, IDB).
assertDOM, network, or visual assertions.
aiVibe-step. AI explores, locks on success.
controlBranching, looping, retry, parallel.
hookWebhooks, Slack, posthog, custom JS.
subEmbed another versioned workflow.

Authoring in code

import { workflow } from '@kifas/sdk';

const wf = workflow('checkout-promo', (b) => {
  const sess  = b.action('spawn', { device: 'iphone-16' });
  const auth  = b.auth('gmail:qa@acme.dev').after(sess);
  const cart  = b.sub('seed-cart@1.2.0').after(auth);
  const ai    = b.ai('Apply promo GUMBO20').after(cart);
  b.assert('response 200').expect('/api/orders').after(ai);
});

await wf.run( { parallels: 5 });

Authoring on the canvas

Drag nodes from the palette, draw edges by holding and dragging from a port. Save publishes a versioned workflow back to your repo as JSON; the diff renders inline so PR review still works.

Conflict resolution. Code-authored and canvas-authored edits to the same node merge cleanly when they touch different fields. When they don't, the editor surfaces a 3-way diff before saving.

Versioning

Workflows are content-addressed by their JSON. Refer to a workflow as name@version (semver), name@latest, or name@sha:abcd…. Sub-workflows pin by exact SHA in CI for hermeticity.

Docs/State handles

State handles

A state handle is a named, immutable snapshot of a browser session — cookies, localStorage, IndexedDB, and (optionally) recorded service-worker responses. It's the missing primitive that turns 30-second logins into 200ms warm starts.

Saving state

await kifas.auth.loginAs('gmail:qa@acme.dev');
await kifas.state.save('admin_logged_in', {
  ttl:    '24h',             // auto-expire
  scope:  'project',         // or 'user', 'org'
  tags:   ['gmail', 'admin'],
});

Loading state

const browser = await kifas.browser.spawn('chromium-131');
await kifas.state.load('admin_logged_in');
// Page is now logged in. Saved you ~30s.

Best practices

  • One handle per role, not per test. admin_logged_in, free_user, paid_user.
  • Re-auth on the schedule, not on every run. Pair auth.loginAs with a 6-hour cron that bumps the handle.
  • Tag aggressively — handles are searchable, so tags: ['org-X'] lets your CI partition fixtures.
Why a primitive, not a fixture? Fixtures live in your test runner. State handles are first-class server objects: visible to MCP, attachable to canvas nodes, refreshable on a schedule, and auditable per project. Same idea, scoped wider.
Docs/Auth Catalog

Auth Catalog

42 pre-built login flows for the providers your stack actually depends on. Each one handles 2FA, captchas (where legal), email codes, magic links, and recovery flows out of the box.

Supported providers

Identity: Google Workspace, Microsoft 365, Okta, Auth0, Apple, Amazon Cognito, Clerk, WorkOS, Stytch.
Consumer: Gmail, GitHub, Apple, Facebook, Instagram, X, LinkedIn, TikTok, Discord, Spotify.
Payments: Stripe, Adyen, Braintree, Plaid, PayPal, Square.
Communications: Twilio, SendGrid, Postmark, Resend, Kifas Inbox.
Custom: bring-your-own with the @kifas/auth-recipe SDK.

Adding a handle

$ npx @kifas/cli auth add okta \
    --domain acme.okta.com \
    --user   qa@acme.dev \
    --totp-secret $OKTA_TOTP \
    --push           # auto-approve push notifs

→ Verified. Catalog handle: okta:qa@acme.dev

Using a handle

await kifas.auth.loginAs('okta:qa@acme.dev', {
  reAuth:  'auto',    // re-login if state expired
  saveAs:  'okta_qa',
});
Privacy. Kifas never reads your real user database. Auth handles run in isolated, ephemeral browsers with the credentials you provided. We store only the resulting cookies, encrypted with project-scoped KMS keys.
Docs/Hybrid engine

Hybrid engine v1.4 · new

AI on the first run, deterministic Playwright on every run after. The hybrid engine is what lets your tests survive a UI redesign without flaking — and stay sub-second at scale.

The lifecycle of an ai.step

  1. First run. Claude Sonnet looks at the rendered page, executes the goal, and emits a deterministic Playwright recipe (getByRole, getByText, click).
  2. Lock. The recipe is committed alongside your test — visible as a 🔒 in the canvas, version-controlled in git.
  3. Replay. Subsequent runs skip the LLM entirely. Cost and latency drop to Playwright baseline.
  4. Self-heal. If the locked recipe fails (UI changed), the engine falls back to AI for that step only, re-locks, and continues. You get a Slack ping with the diff.

Cost model

Step kindCost / stepp50 latency
Locked Playwright$0.0001110ms
AI exploration~$0.0122.4s
Self-heal (rare)~$0.0122.4s

Typical suites are >98% locked after the second CI run.

Docs/Vibe-step

Vibe-step ai.step

A single line of natural language replacing 5–20 lines of brittle selector code. Powered by the hybrid engine, so it gets cheap and fast on the second run.

Examples

// Replaces an entire promo-flow saga
await kifas.ai.step('Apply promo code GUMBO20 and verify the discount line shows -$9.60');

// Form filling
await kifas.ai.step('Fill the signup form with realistic data, agree to ToS, submit');

// Conditional behavior
await kifas.ai.step('If the upsell modal appears, dismiss it; otherwise continue to checkout');

Constraints

You can pin the model, the budget, and the assertion shape:

await kifas.ai.step('Find the cheapest premium plan', {
  model:        'claude-sonnet-4-5',
  maxBudgetUSD: 0.05,
  return:       z.object({ name: z.string(), price: z.number() }),
});
Docs/Emulator fleet

Emulator fleet

120+ browsers, the latest iOS Simulators, and a Pixel/Samsung/OnePlus emulator catalog with hardware-accelerated graphics in every region. Median spawn time: 820ms.

What's in the fleet

FamilyVersionsRegions
Chromium129, 130, 131, 132, beta, canaryus-east-1, eu-west-1, ap-south-1
FirefoxESR 128, 130, 131, 132us-east-1, eu-west-1
WebKit / Safari17.4 → 18.2us-east-1, eu-west-1
iOS Simulator17.5, 18.0, 18.1, 18.2us-east-1 (mac fleet)
Android EmulatorAPI 31–34, Pixel 6/7/8/9us-east-1, eu-west-1, ap-south-1
Real devicesiPhone 13–16, Pixel 6–9, Galaxy S22–S24us-east-1

Why our emulators are faster

Three things: (1) every node in the fleet is GPU-passthrough Vulkan or Metal — no software rasterization; (2) we keep a warm pool sized to your concurrency floor in your nearest region; (3) state handles let you skip the slow part (authenticating + navigating) entirely.

vs. BrowserStack — independent benchmarks (1k runs, parallel 50): Kifas p50 spawn 820ms · BrowserStack 4.8s. Kifas p50 first paint 1.2s · BrowserStack 6.4s.

Choosing a target

// Latest stable Chromium, default region.
await kifas.browser.spawn('chromium-131');

// Specific iOS sim with locale.
await kifas.device.spawn('iphone-16', { os: '18.2', locale: 'en-GB' });

// Real Pixel 9 in us-east.
await kifas.device.spawn('pixel-9', { real: true });
Docs/iOS Simulators

iOS Simulators beta

Run end-to-end tests against real iOS Simulators on Kifas's macOS fleet — including SwiftUI apps, hybrid web views, and Safari with the Web Inspector attached.

Spawning a simulator

const sim = await kifas.device.spawn('iphone-16', {
  os:     '18.2',
  locale: 'en-US',
  app:    './build/Acme.app',    // optional
});

await sim.tap({ accessibilityId: 'sign-in-button' });

App + WebView testing

Kifas ships a Playwright-compatible context that bridges into the Safari WebInspector, so you can drive both native UIKit elements and the embedded web view from the same test.

Docs/Android Emulators

Android Emulators

Pixel 6 → 9 with API levels 31 through 34. Hardware-accelerated, warm-pooled, and reachable from the same SDK call as a Chromium browser.

const dev = await kifas.device.spawn('pixel-9', { api: 34 });
await dev.install('./app-release.apk');
await dev.launch('dev.acme.app');
Docs/Real-device cloud

Real-device cloud

When emulators aren't enough — biometric flows, camera intents, NFC. Kifas's real-device cloud gives you exclusive lease on a physical phone for the duration of your run.

Available on Business and above. Pricing is per device-minute; pool size scales to your concurrency.

Docs/MCP overview

MCP, the way your agent already wants it.

Kifas ships a first-party MCP server: 42 tools, one consistent namespace, scoped to your project. Plug Claude Code, Cursor, Windsurf, or any MCP-compliant client into your test infrastructure with a single command.

One install, every client

$ npx @kifas/mcp@latest --install cursor
$ npx @kifas/mcp@latest --install claude-code
$ npx @kifas/mcp@latest --install windsurf
→ Installed 42 tools. Authenticated as maya@acme.dev.

Tool families

  • Workflow authoring workflow.create, workflow.add_node, workflow.connect, workflow.run.
  • Auth + state auth.list, auth.add, state.save, state.load.
  • Devices browser.spawn, device.list, device.spawn.
  • Run control run.list, run.read, run.replay, run.flake_score.
  • Inboxes inbox.create, inbox.wait_for.

See the full tool reference →

Prompt your agent like a teammate

claude > "We just shipped GUMBO20.
   Write a test that signs in as a fresh user, applies it,
   and asserts the order total. Run it on iPhone 16."

→ Kifas: workflow.create("checkout-promo")
→ Kifas: device.spawn("iphone-16")
→ Kifas: workflow.run() · passed in 4.2s
The MCP card — every Kifas project publishes an mcp.json with the tools it exposes, scoped to that project's permissions. Drop it in your client's MCP config and you're done.
Docs/Install the server

Install the MCP server

Pick your client; we'll do the rest.

Claude Code

$ npx @kifas/mcp@latest --install claude-code

Writes ~/.claude/mcp.json with a project-scoped token. Open Claude Code, type /mcp, and you should see kifas in the list with 42 tools.

Cursor

$ npx @kifas/mcp@latest --install cursor

Adds an entry to Cursor's .cursor/mcp.json. Restart Cursor and the tools appear in the agent panel.

Windsurf

$ npx @kifas/mcp@latest --install windsurf

Manual config

If your client supports MCP but isn't in our installer, point it at:

{
  "mcpServers": {
    "kifas": {
      "command": "npx",
      "args": ["@kifas/mcp@latest"],
      "env": { "KIFAS_API_KEY": "$KIFAS_API_KEY" }
    }
  }
}
Docs/Tool reference

MCP tool reference

All 42 tools, by family. Every tool returns JSON; errors are typed.

Workflow

workflow.createname → workflow_id
Create a new empty workflow.
workflow.add_nodetype, primitive, ai_explore?
Add a node to the open workflow. Returns the node id.
workflow.connectfrom, to, label?
Draw an edge between two nodes.
workflow.runworkflow_id, env?, parallels?
Run the workflow. Returns a run_id and a streaming URL.
workflow.publishworkflow_id, version
Tag a semver version, lockable as name@version.

Auth + state

auth.addprovider, user, secrets
Register a new login handle in the project's Auth Catalog.
auth.list→ AuthHandle[]
List the project's current auth handles.
state.savename, ttl?, scope?
Snapshot the current browser session as a named state handle.
state.loadname
Hydrate a browser with a named state handle.

Devices

browser.spawntarget, region?
Spawn a browser. Targets like chromium-131, firefox-132, webkit-18.
device.spawndevice, os?, real?
Spawn an emulator or real device. Real-device leases are exclusive.
device.list→ Device[]
List all available devices in the fleet.

Run control

run.listfilter?, limit?
List recent runs, filterable by status, branch, or workflow.
run.readrun_id
Read run details: steps, timings, traces, screenshots, AI rationale.
run.replayrun_id, from_step?
Replay a run from a given step. Returns a fresh run id.
run.flake_scoreworkflow_id, window?
Get the rolling 30-day flake score for a workflow.

Inboxes

inbox.createdomain?, alias?
Create a managed inbox under kifas.inbox for transactional email tests.
inbox.wait_forto, timeout
Block until a matching email arrives, or timeout.
Docs/Prompt patterns

Prompt patterns

A short library of prompts that work well with Kifas's MCP server. Steal liberally.

"Test what I just shipped"

claude > "Look at my last commit. Write Kifas tests
   covering the new behavior. Run them. Report which fail."

"Reproduce a flake"

claude > "Workflow 'checkout-regress' has 4% flake.
   Run it 50 times in parallel, find the failing step,
   propose a fix."

"Mass-author from a spec"

claude > "Read /docs/checkout.md. For each user story,
   create a Kifas workflow. Run them on iPhone 16
   and Pixel 9. Group failures by likely root cause."
Docs/API · browser

browser @kifas/sdk

Spawn and control desktop browsers. Returns a Browser handle that's also a Playwright-compatible context.

POSTbrowser.spawn(target, opts?)

const b = await kifas.browser.spawn('chromium-131', {
  region:    'eu-west-1',
  viewport:  { width: 1440, height: 900 },
  locale:    'en-GB',
  proxy:     'tunnel',           // auto-attaches Kifas Tunnel
});
targetstring · required
Browser identifier: chromium-131, firefox-132, webkit-18, or any in the fleet.
opts.regionstring · optional
One of us-east-1, eu-west-1, ap-south-1. Defaults to KIFAS_REGION.
opts.viewport{width, height}
Default viewport for the spawned page.
opts.proxy'tunnel' · optional
Auto-attach Kifas Tunnel — the spawned browser sees your localhost.

GETbrowser.list()

List browsers currently held by your project.

Docs/API · device

device

Spawn iOS Simulators, Android Emulators, and real-device leases.

POSTdevice.spawn(target, opts?)

const d = await kifas.device.spawn('iphone-16', { os: '18.2', real: false });
Docs/API · auth

auth

Login flows from the Auth Catalog.

POSTauth.loginAs(handle, opts?)

await kifas.auth.loginAs('gmail:qa@acme.dev');
Docs/API · state

state

Save and load named browser session snapshots.

POSTstate.save(name, opts?)

POSTstate.load(name)

DELstate.expire(name)

Docs/API · ai

ai

Vibe-step. Natural-language UI actions powered by the hybrid engine.

POSTai.step(goal, opts?)

See Vibe-step for full details.

Docs/API · inbox

inbox kifas.inbox

A managed email inbox for testing transactional flows — invites, magic links, password resets.

POSTinbox.waitFor(opts)

const mail = await kifas.inbox.waitFor({
  to:      /\+invite@acme\.dev/,
  timeout: 30_000,
});
const link = mail.extractLink(/\/accept\?token=/);
Docs/Run in CI

Run in CI

Kifas is just npx playwright test — but with first-class support for parallels, sharding, and trace upload.

GitHub Actions

- name: Kifas tests
  uses: kifas/action@v1
  with:
    api-key: ${{ secrets.KIFAS_API_KEY }}
    parallels: 10

GitLab, CircleCI, Buildkite

Use the SDK directly; the action is just a thin wrapper.

Docs/Kifas Tunnel

Kifas Tunnel

Test localhost from cloud browsers without ngrok, without secrets, without the per-seat tax.

$ npx @kifas/cli tunnel start 3000
→ Forwarding https://acme-3000.tunnel.kifas.io → localhost:3000
Docs/Visual regression

Visual regression

Pixel-perfect diffing with structural awareness — included on every paid plan.

await expect(page).toMatchVisualSnapshot('checkout', {
  threshold: 0.02,            // 2% pixel diff tolerance
  ignore:    ['.timestamp'],
});
Docs/Security

Security & compliance

SOC 2 Type II. GDPR-ready. Private-cloud option on Business+.

  • Data isolation. Every project gets a dedicated KMS key. Auth handles never leave that boundary.
  • Network. Browsers run in single-tenant micro-VMs; egress is denied by default and enforced per-project.
  • Audit log. Every API call, every MCP tool invocation, immutable for 13 months.
  • SSO / SAML / SCIM. Included on Business; no per-seat surcharge.
Docs/Status

Status

Real-time platform status. Subscribe for incident notifications.

API · us-east-1Operational
99.99% over 30d · p50 latency 41ms
Browser fleet · us-east-1Operational
99.97% · spawn p50 820ms
Browser fleet · eu-west-1Operational
99.99% · spawn p50 870ms
iOS Simulator fleetBeta
Capacity-managed; SLA pending GA.
Docs/Your first test

Your first test

A guided walkthrough — pick a real flow from your app, generate a test in 60 seconds, and see how Kifas primitives compose.

See the Quickstart for the 12-line version.