Overview
Zero-code provenance tracking for Node.js — automatic interaction recording for HTTP, Express, Fastify, Prisma, and outbound calls.
Auto-instrumentation patches your Node.js runtime to record provenance interactions automatically. One line at the top of your app — no changes to your application code.
require('@stdiolabs/provenance-sdk/auto').instrument({
apiKey: process.env.PROVENANCE_API_KEY,
origin: 'order-service',
});Every inbound HTTP request, database mutation, and outbound call is tracked as a Provenance interaction — with a shared Unit of Work ID propagated across async boundaries and service calls.
How it works
App startup
→ instrument() patches http, fetch
→ instrumentFastify(app) or express auto-detected
→ AsyncLocalStorage holds UOW context per request
Inbound request arrives
→ http patch intercepts, reads x-uowid / x-userid / x-interaction-timestamp headers
→ express/fastify patch adds route pattern, params, and user
→ Application code runs normally
→ prisma patch captures DB mutations
→ fetch patch injects x-uowid, x-userid, x-interaction-timestamp on outbound calls
→ On response, userId resolved, all buffered interactions flush to Provenance via queue
→ Non-blocking — response already sent to userInteractions are buffered during the request and flushed in a single batch after the response is sent. Your users never wait for Provenance.
Installation
Auto-instrumentation is included in the Node.js SDK — no extra package needed.
npm install @stdiolabs/provenance-sdkQuick start
Add this as the first line of your application, before importing Express, Prisma, or any other framework:
// server.js — first line
const { instrument } = require('@stdiolabs/provenance-sdk/auto');
instrument({
apiKey: process.env.PROVENANCE_API_KEY,
origin: 'order-service',
});
// Now import your app
const express = require('express');
const app = express();
// ... your routesThat's it. Every request to your Express app is now tracked.
What gets tracked
| Layer | What it captures | Patched module |
|---|---|---|
| Inbound HTTP | Every incoming request with method, path, status, duration | http.Server |
| Express routes | Route pattern, params, req.user, req.body | express.Router |
| Fastify routes | Route pattern, params, request.user, request.body | Fastify hooks |
| Outbound HTTP | Every fetch() and http.request/https.request call with URL, method, status, duration. Covers axios, got, node-fetch, and any library built on Node's http module | globalThis.fetch, http.request, https.request |
| Prisma ORM | Every create, update, delete with model name and data | prisma.$use() |
| Sequelize ORM | Every create, update, destroy, find with model name and data | Sequelize global hooks |
End-to-end example
Given this application code (no provenance-specific code anywhere):
// server.js
require('@stdiolabs/provenance-sdk/auto').instrument({
apiKey: process.env.PROVENANCE_API_KEY,
origin: 'order-service',
routes: {
'POST /api/orders': { resourceType: 'ORDER', action: 'PLACE' },
},
});
const express = require('express');
const app = express();
app.use(express.json());
app.post('/api/orders', async (req, res) => {
const order = await prisma.order.create({
data: { userId: req.user.id, total: req.body.total, status: 'pending' },
});
await fetch('http://payment-service:4001/api/charges', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ orderId: order.id, amount: order.total }),
});
res.json(order);
});A POST /api/orders request produces these interactions, all under the same UOW:
| # | Resource Type | Resource ID | Action | Metadata |
|---|---|---|---|---|
| 1 | ORDER | order-501 | CREATE | model: Order, operation: create |
| 2 | CHARGE | order-501 | CREATE | direction: outbound, url: payment-service |
| 3 | ORDER | order-501 | PLACE | route: POST /api/orders, status: 200, duration: 247ms |
For a nested route like PUT /api/orders/ord-1/items/item-2:
| # | Resource Type | Resource ID | Action | Metadata |
|---|---|---|---|---|
| 1 | ITEM | item-2 | UPDATE | route: PUT /api/orders/:orderId/items/:itemId, parent: { ORDER, ord-1 } |
The Prisma create is tracked at the ORM level. The outbound fetch is tracked with the injected x-uowid header (so payment-service's own instrumentation continues the same UOW). The HTTP-level interaction captures the overall request.