Provenance
EcosystemAuto-Instrumentation

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 user

Interactions 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-sdk

Quick 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 routes

That's it. Every request to your Express app is now tracked.

What gets tracked

LayerWhat it capturesPatched module
Inbound HTTPEvery incoming request with method, path, status, durationhttp.Server
Express routesRoute pattern, params, req.user, req.bodyexpress.Router
Fastify routesRoute pattern, params, request.user, request.bodyFastify hooks
Outbound HTTPEvery 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 moduleglobalThis.fetch, http.request, https.request
Prisma ORMEvery create, update, delete with model name and dataprisma.$use()
Sequelize ORMEvery create, update, destroy, find with model name and dataSequelize 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 TypeResource IDActionMetadata
1ORDERorder-501CREATEmodel: Order, operation: create
2CHARGEorder-501CREATEdirection: outbound, url: payment-service
3ORDERorder-501PLACEroute: POST /api/orders, status: 200, duration: 247ms

For a nested route like PUT /api/orders/ord-1/items/item-2:

#Resource TypeResource IDActionMetadata
1ITEMitem-2UPDATEroute: 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.