Provenance
EcosystemAuto-Instrumentation

Configuration

Full configuration reference for auto-instrumentation — enrichment, userId resolution, environment variables, and limitations.

Configuration reference

instrument({
  // ─── Required ───
  apiKey: 'your-api-key',           // or PROVENANCE_API_KEY env var
  origin: 'my-service',             // or PROVENANCE_ORIGIN env var

  // ─── Platform ───
  platformUrl: 'https://...',       // default: https://platform.provenance.dev
  apiUrl: 'https://...',            // explicit API URL (skips platform resolution)
  allowLocalhost: false,            // allow localhost URLs (dev only)

  // ─── Patches ───
  instrumentations: ['http', 'http-outbound', 'express', 'fastify', 'fetch'],  // which modules to patch

  // ─── Enrichment ───
  enrich: (ctx) => ({ ... }),        // common fields merged into every interaction

  // ─── Resolution ───
  routes: { ... },                  // route pattern → { resourceType, action }
  models: { ... },                  // ORM model name → { resourceType } or null
  actions: { ... },                 // HTTP verb → action override
  topics: { ... },                  // message topic → { resourceType, action } or null
  outbound: { ... },                // hostname → { resourceType, action }
  resourceId: { ... },              // resource type → (ctx) => id
  userId: (ctx) => '...',           // user ID resolver function
  resolve: (ctx) => { ... },        // full override function
  exclude: [ ... ],                 // paths to skip
});

User ID resolution

The auto-instrumentation resolves the user performing each interaction through a chain — first match wins:

  1. x-userid header — if the incoming request includes this header, it's used directly
  2. userId() resolver function — a configurable function you provide in the instrument() config
  3. req.user.id — fallback to the id property of req.user (set by auth middleware like Passport, express-jwt, etc.)

Default behavior

With no configuration, the userId is resolved from the x-userid header or req.user.id:

instrument({
  apiKey: process.env.PROVENANCE_API_KEY,
  origin: 'order-service',
});

// If your auth middleware sets req.user = { id: 'user-123', email: '...' }
// → userId resolves to 'user-123' automatically

Custom userId resolver

When your user object doesn't have an id field, or you want to use a different property:

instrument({
  apiKey: process.env.PROVENANCE_API_KEY,
  origin: 'order-service',

  // Extract userId from your auth middleware's user object
  userId: (ctx) => ctx.user?.sub,           // JWT subject claim
  // or
  userId: (ctx) => ctx.user?.email,         // use email as identifier
  // or
  userId: (ctx) => ctx.user?.uid,           // Firebase UID
  // or
  userId: (ctx) => ctx.headers['x-forwarded-user'],  // proxy header
});

The ctx object passed to the resolver is the same request context used everywhere else — it has user, headers, params, body, etc.

The resolver runs when req.user becomes available (after your auth middleware) and again at flush time. The x-userid header always takes priority — if it's set, the resolver is not called.

Enrichment

Add common fields to every interaction's data object — IP address, session ID, environment, or anything else from the request context:

instrument({
  apiKey: process.env.PROVENANCE_API_KEY,
  origin: 'order-service',

  enrich: (ctx) => ({
    ip: ctx.headers['x-forwarded-for']?.split(',')[0]?.trim(),
    sessionId: ctx.headers['x-session-id'],
    environment: process.env.NODE_ENV,
  }),
});

The enrich function is called once per request at flush time. The returned object is merged into every interaction's data — HTTP, Prisma, fetch, and manual track() calls all get the same fields.

Fields from the individual interaction take priority over enriched fields. If a Prisma interaction already has { model: 'Order', operation: 'create' } and your enrich function returns { ip: '1.2.3.4', model: 'override' }, the result is { ip: '1.2.3.4', model: 'Order', operation: 'create' } — the interaction's own model wins.

The ctx object is the same request context available everywhere else:

enrich: (ctx) => ({
  // From headers
  ip: ctx.headers['x-forwarded-for']?.split(',')[0]?.trim(),
  sessionId: ctx.headers['x-session-id'],
  correlationId: ctx.headers['x-correlation-id'],

  // From auth
  tenantId: ctx.user?.tenantId,
  userRole: ctx.user?.role,

  // From environment
  environment: process.env.NODE_ENV,
  region: process.env.AWS_REGION,
  version: process.env.APP_VERSION,

  // From request
  userAgent: ctx.headers['user-agent'],
})

If the enrich function throws, the error is silently caught — interactions are still flushed without the enriched fields. This ensures enrichment never breaks your application.

Environment variables

VariableDescription
PROVENANCE_API_KEYAPI key (used if apiKey not passed to instrument())
PROVENANCE_ORIGINOrigin name (used if origin not passed)

Limitations

  • Node.js only — auto-instrumentation uses AsyncLocalStorage and module patching, which are Node.js-specific. Python and Java equivalents are planned.
  • ESMrequire('@stdiolabs/provenance-sdk/auto') works with CommonJS. For ESM apps, use --require provenance-sdk/auto in your Node.js flags or import before other modules.
  • Fastify — requires explicit instrumentFastify(app) call because Fastify hooks are per-instance.
  • Prisma — requires explicit instrumentPrisma(client) call because the Prisma client is user-instantiated.
  • Sequelize — requires explicit instrumentSequelize(sequelize) call because the Sequelize instance is user-created.
  • Body capture — request bodies are only captured for POST, PUT, and PATCH requests with JSON content.
  • Response capture — response bodies are captured for resource ID extraction but not stored in the interaction metadata (to avoid logging sensitive data).