| name | sanity-check |
| description | Run an E2E smoke test that installs rivetkit packages from npm in an isolated temp project, starts the hello-world counter actor, then verifies both HTTP actions and WebSocket actions + events work end-to-end. Use when the user asks to sanity check, smoke test, or verify a rivetkit release/preview-publish works. |
Sanity Check
Run a quick end-to-end sanity check of a published rivetkit version: copy the hello-world example to a temp directory, install the specified package version from the public npm registry, start the dev server, and run a client test script that verifies both HTTP actions and WebSocket connections with event broadcasting.
When to use
- User wants to verify a published rivetkit version works (e.g.,
rivetkit@0.0.0-pr.4701.a818b77, rivetkit@latest, rivetkit@2.5.0)
- After a preview publish to verify the build is functional
- After a release to verify the package installs and runs correctly
- User says "sanity check", "smoke test", "verify the build", or "test this version"
Inputs
- Version or tag (required) — explicit pkg-pr-new preview, npm dist-tag, or semver. If not provided in the user's message, ask for it.
- Additional test behavior (optional) — e.g., "also verify workflows persist" or "check that KV works." If provided, extend
src/index.ts + test.mjs using the menu in "Extending with custom tests" below.
Usage
/sanity-check <version> — run in a temp directory on the host
/sanity-check docker <version> — run inside a node:22 Docker container
/sanity-check <version> <custom instructions> — any extra instructions (e.g. "also hit a KV action", "verify state persists across restart", "use pnpm", "test on node 20")
<version> is any npm-resolvable spec: an explicit pkg-pr-new preview (0.0.0-pr.4701.a818b77), an npm dist-tag (latest, rc, next), or a semver (2.3.0-rc.4). If the user omits it, ask for it.
What it tests
npm install of rivetkit, @rivetkit/react, and the platform-specific @rivetkit/rivetkit-napi-* native binding from the public npm registry
- Boot the hello-world counter actor server via
registry.start() on port 6420
- HTTP path: call
counter.increment(5), counter.increment(3), counter.getCount() and assert the values
- WebSocket path: open a
.connect() conn, subscribe to the newCount event, call increment(10), assert the action response AND the broadcast event value match
- Report the resolved versions of
rivetkit + @rivetkit/rivetkit-napi
Requirements
- Node.js 22+ (or Docker with
node:22)
- This repo available locally — only used to copy
examples/hello-world/src/ as the seed. Does NOT use any of its node_modules or workspace links.
Steps
1. Set up the test project
REPO_ROOT=$(git rev-parse --show-toplevel)
SANITY_DIR=$(mktemp -d /tmp/rivetkit-sanity-XXXXXX)
cp -r "$REPO_ROOT/examples/hello-world/src" "$REPO_ROOT/examples/hello-world/tsconfig.json" "$SANITY_DIR/"
cd "$SANITY_DIR"
Write package.json with <VERSION> substituted:
{
"name": "rivetkit-sanity",
"private": true,
"type": "module",
"dependencies": {
"rivetkit": "<VERSION>",
"@rivetkit/react": "<VERSION>"
},
"devDependencies": {
"tsx": "^4",
"typescript": "^5"
}
}
For pkg-pr-new previews, <VERSION> can be the bare version string (0.0.0-pr.4701.a818b77) — npm resolves it directly. If that fails, fall back to the URL form: "rivetkit": "https://pkg.pr.new/rivet-dev/rivet/rivetkit@<short-sha>".
2. Write test.mjs
import { createClient } from "rivetkit/client";
import { spawn } from "node:child_process";
const ENDPOINT = "http://localhost:6420";
console.log("Starting counter actor server...");
const server = spawn("npx", ["tsx", "src/index.ts"], {
stdio: ["ignore", "pipe", "pipe"],
});
let log = "";
server.stdout.on("data", (d) => (log += d));
server.stderr.on("data", (d) => (log += d));
async function waitForServer(timeoutMs = 30000) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
try {
const r = await fetch(`${ENDPOINT}/health`);
if (r.ok) ;
} {}
( (r, ));
}
.( + log);
();
}
exitCode = ;
{
();
client = ();
.();
counter = client..([]);
a = counter.();
b = counter.();
c = counter.();
(a !== ) ();
(b !== ) ();
(c !== ) ();
.();
.();
ws = client..([]).();
( {
t = ( ( ()), );
ws.( {
(t);
();
});
ws.(rej);
});
eventPromise = ( {
t = ( ( ()), );
ws.(, {
(t);
(v);
});
});
wsCount = ws.();
eventValue = eventPromise;
(wsCount !== ) ();
(eventValue !== ) ();
.();
ws.();
.();
} (err) {
.();
.( + log.(-));
exitCode = ;
} {
server.();
process.(exitCode);
}
3. Install + run
Default (host):
cd "$SANITY_DIR"
npm install
node test.mjs
If you need to inspect a failure after the fact, tee the output:
node test.mjs 2>&1 | tee /tmp/sanity-check.log
echo "exit=$?"
Docker mode:
docker run --rm \
-v "$SANITY_DIR":/app \
-w /app \
node:22 \
bash -c "npm install && timeout 120 node test.mjs"
4. Report installed versions
Surface the resolved versions (rivetkit's exports doesn't expose ./package.json, so read the file directly):
node -e "
for (const p of ['rivetkit','@rivetkit/react','@rivetkit/rivetkit-napi']) {
try {
const v = JSON.parse(require('fs').readFileSync('node_modules/'+p+'/package.json','utf8')).version;
console.log(p, v);
} catch (e) { console.log(p, '(not installed:', e.code || e.message, ')'); }
}
"
5. Report results
Tell the user:
- Resolved versions (rivetkit + @rivetkit/rivetkit-napi)
- HTTP path results
- WebSocket path results + event value
- ✅ or ❌ with the last 2KB of server log on failure
6. Clean up
rm -rf "$SANITY_DIR"
Extending with custom tests
If the user asks for extra behavior, modify src/index.ts and add assertions to test.mjs before running. Common asks and where to slot them:
- KV round-trip: add
kv actions (setKv: (c, key, val) => c.kv.set(key, val), getKv: (c, key) => c.kv.get(key)), then in test.mjs call set → get and assert.
- Workflow: add a workflow action, await its completion, assert the final state.
- SQLite + migrations: add a migration and a query action, call it, assert rows.
- State persistence: increment, kill the server (
server.kill("SIGTERM")), await exit, respawn, call getCount(), assert value preserved.
- Multiple actor instances: use different keys, verify isolation.
Start from the base test above; layer additions rather than rewriting it.
Rules
- Always use a fresh temp directory — never run in the repo itself.
- Always install from the public npm registry or pkg-pr-new — never use local workspace links /
file: deps.
- Pin
rivetkit + @rivetkit/react to the exact user-specified version; let npm's optionalDependencies resolve the right @rivetkit/rivetkit-napi-<platform> binary automatically.
- If
npm install fails to resolve a bare pkg-pr-new version string, retry using the https://pkg.pr.new/rivet-dev/rivet/<pkg>@<short-sha> URL form.
- If the server doesn't reach
/health in 30s, dump the last 2KB of server stderr/stdout before failing — most install/runtime issues show up there (missing native binary, wrong Node version, port collision).
- On Docker mode, run
rm -rf $SANITY_DIR only after docker run --rm exits so container-created node_modules get cleaned by the --rm flag.