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.
NS_COOKIE_DOMAIN (Production optional)
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.