This is the tutorial version of a promise we make on the homepage, so let's hold ourselves to a clock. Starting point: a Node backend (any framework), a user object with an id, and 30 minutes. End point: eight security-relevant events flowing, and a customer-facing audit page your next security questionnaire can screenshot.
If you'd rather understand the schema thinking first, read the actor/action/target post — but you don't need it to follow along.
Minutes 0–3: install and initialize
# ~3 min — install and configure npm install @softechlog/node # .env — from the dashboard API Keys page after sign-up SOFTECHLOG_SECRET_KEY=stl_sk_xxxxxxxxxxxx
// lib/softechlog.ts — ~1 min
import { Softechlog } from '@softechlog/node';
export const log = new Softechlog({
secretKey: process.env.SOFTECHLOG_SECRET_KEY!,
});Sign-up and project creation are part of the three minutes — the onboarding wizard hands you the secret key and then waits for your first event, which is convenient for step 4.
Minutes 3–18: the eight events that matter
Resist the urge to instrument everything. Eight events at your backend's choke points answer the overwhelming majority of "who did what" questions:
// ~15 min — one line at each choke point in your service layer.
// These eight cover most of what customers and auditors ever ask for:
// 1+2. Authentication (in your login handler / auth callback)
log.track({ actor, action: 'auth.login.succeeded', metadata: { method: 'google' } });
log.track({ actor: { id: attemptedEmail }, action: 'auth.login.failed' });
// 3+4. Membership (invite + removal handlers)
log.track({ actor, action: 'member.invited', target: ws, metadata: { invited_email, role } });
log.track({ actor, action: 'member.removed', target: ws, metadata: { removed_email } });
// 5. Authorization changes (role-change handler)
log.track({ actor, action: 'member.role.changed', target: ws, metadata: { from, to } });
// 6+7. Credentials (API-key create/revoke handlers)
log.track({ actor, action: 'api_key.created', target: { type: 'api_key', id: key.id } });
log.track({ actor, action: 'api_key.revoked', target: { type: 'api_key', id: key.id } });
// 8. Data movement (export handler)
log.track({ actor, action: 'data.exported', metadata: { format, rows: count } });// Tip: one helper keeps actors consistent everywhere
const asActor = (user: User) => ({
id: String(user.id),
name: user.name,
email: user.email,
});Two properties make this safe to sprinkle liberally: track() never throws, and it's fire-and-forget — a logging hiccup can't fail a customer request. (On serverless, await log.flush() before returning; details in the Next.js guide.)
Minutes 18–23: the feed token endpoint
The audit page runs in your customer's browser, and browsers never get the secret key. Instead your server mints a short-lived token scoped to exactly one user:
// ~5 min — the read side: a feed token endpoint
// app/api/activity-token/route.ts (or an Express route)
import { log } from '@/lib/softechlog';
export async function GET(req: Request) {
const user = await requireUser(req); // your auth
const { token } = await log.feedToken({ actorId: String(user.id), ttlSeconds: 3600 });
return Response.json({ token });
}The scope decision (actorId) comes from your auth, not from anything the client sends — that's what makes the page IDOR-proof by construction.
Minutes 23–28: the audit page
<!-- ~5 min — the audit page itself. Works in any framework. -->
<script src="https://softechlog.com/feed.js"></script>
<h2>Account activity</h2>
<softechlog-feed id="audit"></softechlog-feed>
<script>
fetch('/api/activity-token')
.then((r) => r.json())
.then(({ token }) => document.getElementById('audit').setAttribute('feed-token', token));
</script><softechlog-feed> is a Web Component — loading, empty, error, and pagination states are handled. Theming attributes are in the component docs when you want it to match your UI.
Minutes 28–30: verify
# ~2 min — verify end to end # 1. Perform a tracked action in your app (invite someone, rotate a key) # 2. Watch it arrive: dashboard → Events → Live # 3. Load your new audit page — the event is in the feed # 4. Prove export works while you are at it: curl -H "Authorization: Bearer $SOFTECHLOG_SECRET_KEY" \ "https://api.softechlog.com/v1/events/export?format=csv" -o audit.csv
What you just avoided
The half hour above replaces the schema design, ingest validation, cursor pagination, scoped-read security model, retention job, and export endpoint you'd otherwise build — about 10 engineering weeks by our line-by-line estimate. The free tier (10k events/month) is enough to run this whole tutorial and then some; nothing above required a paid plan.
Where to go next: wire your remaining sensitive actions incrementally (billing changes and deletions are usually the next two customers ask about), add browser auto-capture if support wants full session timelines, and read what an audit trail needs before SOC 2 if there's a questionnaire on the horizon.