Session log — private content reviewer

← All session logs

Session log — private content reviewer

25 April 2026 · Hasmukh with Claude · one focused session, end-to-end build of a self-hosted content reviewer at reviewer.s2l.online.

Brief

1. Why a private reviewer

AI tools default to lazy. They take shortcuts and ship the obvious answer because that is how they are trained. The reviewer is a way to front-load everything Claude needs to know about Hasmukh’s voice, the S2L audience, the channel, and the house standards, so that the prompt that lands in Claude cannot be skimmed.

A version of this tool already exists at reviewer.elmspark.com, built by Kenn at ElmsPark for Hasmukh. The brief was to build Hasmukh’s own copy on his SA VPS, branded for S2L, and structured so that future team members or partners can be added in about five minutes each.

Decision

2. The stack and the architecture call

Static HTML, no build step. React 18 and Babel standalone via the unpkg CDN, so a single file edit and a hard reload is the whole development loop. Bunny Fonts, never Google Fonts — privacy and GDPR matter for the funder audience as much as the learner audience.

The architecture choice was shared bundle vs self-contained per page. The shared bundle won. The React app lives once at /static/app.js. Each user page is a thin shell that defines a window.CLIENT_CONFIG object — title, brandmark, palette, channels, criteria, and a per-client buildPrompt function — then loads the shared bundle. Adding a new team member becomes copy-the-shell, swap the preamble and palette, drop in a new htpasswd. About five minutes.

Step 1

3. DNS, web root, file layout

DNS for s2l.online is at domains.co.za, so Claude could not add the A record itself. Hasmukh added reviewer → 154.66.198.194 manually. Propagation was effectively instant on 1.1.1.1 and 8.8.8.8.

Web root at /var/www/reviewer.s2l.online/ with three pieces:

  • static/app.js — the shared React bundle, public, no auth
  • index.html — the admin TOC, behind admin auth
  • hasmukh/index.html — the S2L-branded reviewer, behind hasmukh auth
Step 2

4. Auth pattern, longest-prefix matching

Three auth zones, enforced by nginx’s longest-prefix matching, with the ^~ modifier to make the intent explicit:

  • /static/ is public so the bundle can be cached and shared.
  • /hasmukh/ is gated by reviewer-hasmukh.htpasswd, which contains both hasmukh and admin. That second user is the override — admin can preview every reviewer page without learning each person’s password.
  • Everything else, including the TOC, is gated by reviewer-admin.htpasswd, which contains only admin.

Both htpasswd files use bcrypt (htpasswd -B), live in /etc/nginx/auth/, are owned root:www-data, mode 640. Passwords were generated on the VPS itself with tr -dc "A-Za-z0-9" < /dev/urandom and never written into any HTML file.

Step 3

5. Building the React bundle and the Hasmukh shell

The React app is roughly 200 lines. Three steps on screen: pick a channel, paste a draft, copy a tailored prompt. Channels render as cards in a 3×3 grid that collapses to 2× on tablets and 1× on phones — mobile-first, because S2L’s audience is.

The Hasmukh shell defines the S2L brandmark (S in red, 2 in blue, L in green, Outfit 800, letter-spacing -1.5px), the cool cream gradient background, blue accents, and the full S2L preamble inside buildPrompt. Nine channels were defined, in the order Hasmukh specified: WhatsApp, LinkedIn, Funder email, Course description, Impact brief, Community post, Community flyer, Newsletter, Partner pitch.

Step 4

6. Deploy, nginx vhost, Let’s Encrypt

Files copied via scp, ownership set to www-data. New nginx vhost in /etc/nginx/sites-available/reviewer.s2l.online, listening on port 80 only at first. Four security headers applied to every response, including 401s:

  • X-Frame-Options: SAMEORIGIN
  • X-Content-Type-Options: nosniff
  • X-Robots-Tag: noindex, nofollow
  • Referrer-Policy: strict-origin-when-cross-origin

Certbot was run with --nginx --redirect --email hasmukh@gajjar.co.za. Cert was issued on the first attempt, valid until 24 July 2026, with auto-renewal scheduled. None of the existing vhosts (s2l.online, tibaai, docs, cpd) were touched.

Step 5

7. Verification, eight curl checks and a screenshot

Every acceptance criterion in the brief was checked, not assumed:

  • HTTP redirects to HTTPS → 301 confirmed
  • HTTPS no creds on / → 401 confirmed
  • HTTPS no creds on /hasmukh/ → 401 confirmed
  • HTTPS no creds on /static/app.js → 200 confirmed
  • HTTPS admin creds on / → 200 confirmed
  • HTTPS hasmukh creds on /hasmukh/ → 200 confirmed
  • HTTPS hasmukh creds on / → 401 confirmed (correct, hasmukh is not in the admin file)
  • HTTPS admin creds on /hasmukh/ → 200 confirmed (admin override works)
  • HTTPS wrong creds → 401 confirmed
  • All four security headers present in the response

The reviewer was then rendered in headless Chrome at 1280×1800 with the hasmukh creds, and the screenshot confirmed the brandmark, palette, and 3×3 channel grid on the live site.

Step 6

8. First real use, a WhatsApp content review

Hasmukh used the live tool immediately. Channel: WhatsApp. Content: “I am not happy with the s2l.online site as when I register and try a course it does not access the course and gives login again.”

The reviewer flagged it as the wrong genre for the channel — a self-addressed bug report, not a WhatsApp message for the S2L audience — and recommended Option C, rethink the angle. Two improved drafts were offered: a transparency note from Hasmukh to the community, and a normal drive-to-course message.

The tool is doing what it was designed to do. It refused to dress up source material that was structurally wrong for the channel, and instead asked Hasmukh to clarify intent first.

Operational

9. Operational thread, the login-loop bug

The content Hasmukh pasted was also a real bug report. He registered on s2l.online, tried to access a course, and was bounced back to the login screen. This is the same flow that was wired up in the morning session (see the earlier 25 April log, “Registration and login flow finally wired up”), so either the wiring did not survive, or there is a second issue downstream of registration when a learner attempts to enrol.

Scar tissue Reported but not yet investigated in this session. Needs a separate look at the EP Membership configuration, the course-access shortcode behaviour, and whether enrolment is being created on registration or only on first lesson view.
Open

10. Still open

  • Investigate the login-loop bug on s2l.online (above).
  • Add team-member or partner reviewer pages on request — about five minutes each.
  • The shared bundle is loaded with type="text/babel" and compiled in the browser. That is fine for a private tool, but if the bundle ever grows beyond the trivial, pre-compiling to a plain JS file would speed first paint on slow connections.