Skip to main content

Installation & Environment Variables

:::caution Pre-release This is a pre-1.0 library (v0.1.1) — API may change without notice. :::

Installation

Install the package via npm or pnpm:

npm install @next-story/journey-recorder
# or
pnpm add @next-story/journey-recorder

The package exports three entry points (via subpath imports):

  • @next-story/journey-recorder/client — browser-safe SDK for client components
  • @next-story/journey-recorder/server — Edge-safe middleware and collectors
  • @next-story/journey-recorder/shared — shared types and utilities

Environment Variables

Your Next.js application must set these secrets in production. The demo app (apps/demo/.env.example) shows the variable names:

NS_TOKEN_SECRET (Required)

A base64-encoded 32-byte secret used to sign and verify journey tokens issued by the middleware.

Generate one:

node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

Store it in your .env.local:

NS_TOKEN_SECRET=<your-base64-secret>

NS_TOKEN_SECRET_PREVIOUS (Production rotation)

When rotating NS_TOKEN_SECRET, set this to the old secret so tokens minted under the previous secret continue to verify during the rotation window (typically 24 hours).

Once all in-flight tokens have expired or been reissued, clear this variable.

NS_TOKEN_SECRET_PREVIOUS=<old-base64-secret>

NS_IP_SALT (Required)

A secret salt used for daily-rotating-salt IP hashing (see Privacy & Data Model).

Generate one:

node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

Store it:

NS_IP_SALT=<your-base64-salt>

The IP is never stored raw. Instead, it is hashed with this salt and the current UTC day, making the hash uncorrelated across days and impossible to reverse.

The domain on which the __ns_aid (anonymous ID) and __ns_token cookies are set.

NS_COOKIE_DOMAIN=example.com

If unset, cookies default to localhost in development or infer from the request origin in production.

Example .env.local

# Token signing
NS_TOKEN_SECRET=dGhpcyBpcyBhIDMyLWJ5dGUgc2VjcmV0IGZvciBkZXY=
NS_TOKEN_SECRET_PREVIOUS=

# IP hashing salt
NS_IP_SALT=c2FsdCBmb3IgZGFpbHktcm90YXRpbmcgaGFzaA==

# Cookie domain (omit for localhost)
# NS_COOKIE_DOMAIN=example.com

Usage in Middleware and Handlers

Pass the secrets to withJourneyToken and createCollectHandler:

// middleware.ts
import { withJourneyToken } from "@next-story/journey-recorder/server";

return withJourneyToken({
request,
response,
tokenSecrets: {
current: process.env.NS_TOKEN_SECRET!,
previous: process.env.NS_TOKEN_SECRET_PREVIOUS,
},
// ... siteResolver, ...
});

// app/api/journey/collect/route.ts
import { createCollectHandler } from "@next-story/journey-recorder/server";

const handler = createCollectHandler({
tokenSecrets: {
current: process.env.NS_TOKEN_SECRET!,
previous: process.env.NS_TOKEN_SECRET_PREVIOUS,
},
ipSalt: process.env.NS_IP_SALT!,
// ... sink, ...
});

Both current secret and ipSalt are required at runtime. previous is optional and used only during rotation.

Verification

Once configured, verify the middleware is working by checking the response headers:

curl -i https://localhost:3000/

Look for a Set-Cookie: __ns_token=... header in the response.

Verify the collect route:

curl -X POST https://localhost:3000/api/journey/collect \
-H "Content-Type: application/json" \
-d '{"events":[],"sessionId":"test"}'

Expect a 200 response if the token is valid.