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(); });
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.
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
ai.step). Click any failed step to replay it from that exact frame.5. Where to go next
- Workflows — for tests too complex to live in one file.
- Run in CI — works in 4 lines on GitHub Actions.
- Hand the keyboard to Claude — author tests via prompts.
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.
| Variable | Required? | Description |
|---|---|---|
KIFAS_API_KEY | Yes (CI) | Project-scoped API key. Rotate from Settings → API. |
KIFAS_PROJECT | Optional | Override the project the key resolves to. |
KIFAS_REGION | Optional | us-east-1 (default), eu-west-1, ap-south-1. |
KIFAS_TUNNEL | Optional | Auto-attach Kifas Tunnel for localhost testing. |
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
| Type | Stripe | Purpose |
|---|---|---|
action | ● | Browser actions: navigate, click, type, scroll. |
auth | ● | Login via Auth Catalog. Returns a state handle. |
state | ● | Save / load named browser state (cookies, IDB). |
assert | ● | DOM, network, or visual assertions. |
ai | ◉ | Vibe-step. AI explores, locks on success. |
control | ● | Branching, looping, retry, parallel. |
hook | ● | Webhooks, Slack, posthog, custom JS. |
sub | ● | Embed 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.
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.
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.loginAswith a 6-hour cron that bumps the handle. - Tag aggressively — handles are searchable, so
tags: ['org-X']lets your CI partition fixtures.
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', });
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
- First run. Claude Sonnet looks at the rendered page, executes the goal, and emits a deterministic Playwright recipe (
getByRole,getByText,click). - Lock. The recipe is committed alongside your test — visible as a 🔒 in the canvas, version-controlled in git.
- Replay. Subsequent runs skip the LLM entirely. Cost and latency drop to Playwright baseline.
- 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 kind | Cost / step | p50 latency |
|---|---|---|
| Locked Playwright | $0.0001 | 110ms |
| AI exploration | ~$0.012 | 2.4s |
| Self-heal (rare) | ~$0.012 | 2.4s |
Typical suites are >98% locked after the second CI run.
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() }), });
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
| Family | Versions | Regions |
|---|---|---|
| Chromium | 129, 130, 131, 132, beta, canary | us-east-1, eu-west-1, ap-south-1 |
| Firefox | ESR 128, 130, 131, 132 | us-east-1, eu-west-1 |
| WebKit / Safari | 17.4 → 18.2 | us-east-1, eu-west-1 |
| iOS Simulator | 17.5, 18.0, 18.1, 18.2 | us-east-1 (mac fleet) |
| Android Emulator | API 31–34, Pixel 6/7/8/9 | us-east-1, eu-west-1, ap-south-1 |
| Real devices | iPhone 13–16, Pixel 6–9, Galaxy S22–S24 | us-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.
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 });
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.
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');
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.
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.
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
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.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" }
}
}
}MCP tool reference
All 42 tools, by family. Every tool returns JSON; errors are typed.
Workflow
run_id and a streaming URL.name@version.Auth + state
Devices
chromium-131, firefox-132, webkit-18.Run control
Inboxes
kifas.inbox for transactional email tests.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."
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 });
us-east-1, eu-west-1, ap-south-1. Defaults to KIFAS_REGION.GETbrowser.list()
List browsers currently held by your project.
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 });
auth
Login flows from the Auth Catalog.
POSTauth.loginAs(handle, opts?)
await kifas.auth.loginAs('gmail:qa@acme.dev');
state
Save and load named browser session snapshots.
POSTstate.save(name, opts?)
POSTstate.load(name)
DELstate.expire(name)
ai
Vibe-step. Natural-language UI actions powered by the hybrid engine.
POSTai.step(goal, opts?)
See Vibe-step for full details.
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=/);
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.
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
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'], });
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.
Status
Real-time platform status. Subscribe for incident notifications.
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.