Thnkrr — Where Things Stand, 29 September 2026
What this is: a status report on the four features built in this run of work. Each feature becomes a pair of pull requests (PRs: requests to add finished code to the main version): one for the server code (the API) and one for the web app. Below, "PR 1" means that pair. For each feature it covers what we built, what is still a stand-in, and what ships.
A few words used below:
- Branch: a separate copy of the code where one feature is built without touching the main code.
- Stub: a stand-in that lets us test on a laptop before the real thing exists. For example, "every post is approved automatically". Each stub has a switch, and each one is forced off in production unless the table says otherwise.
- Migration: a script that adds new tables or columns to the database. All of ours only add; none of them change or delete existing data.
- Commit: one saved batch of changes. Merge: adding a branch's code into another branch. Pushed: copied up to GitHub, our online code store.
- Production (live): the real server students will use. Localhost: the app running on your own computer.
In one minute
- Four features are built and tested: moderation before the pilot (with care referrals), photos in posts, parental consent, and video in posts.
- None of it is live, and none of it is in the main code yet. Nothing has been
merged into
main. - Pushed to GitHub: photos (all of it) and most of moderation.
- Not pushed: consent, video, the combined test branch, and moderation's two newest pieces (the care referrals, and today's doc updates). Nothing gets pushed without your go-ahead.
- All four run together on one local branch,
dev/pilot, which is what localhost runs for testing. - What's left is mostly outside the code: founder decisions, free applications to child-protection bodies, supplier quotes, and setting up the live server. The code still to write is listed under each feature.
The four PRs at a glance
| # | Feature | Decision | Branch | Built on | Pushed? |
|---|---|---|---|---|---|
| 1 | Moderation before the pilot + care referrals | D-67 (plus the research in docs/20) | feat/moderation-pre-pilot | main | Mostly. Not yet pushed: the care referrals (both repositories) and today's doc updates (API) |
| 2 | Photos in posts | D-66 | feat/post-images | main | Yes, fully |
| 3 | Video in posts | D-69 | feat/post-videos | PR 2 (it needs photos) | No |
| 4 | Parental consent | D-68 | feat/parental-consent | main | No |
| — | Everything together, for testing | — | dev/pilot | all four | No (never becomes a PR) |
Suggested merge order: 1 → 2 → 3 → 4.
- Moderation goes first. It is the biggest, the oldest, and the others were tested on top of it.
- Video goes straight after photos, because it is built on top of photos. Once photos is
in
main, the video PR shows only the video changes. - Consent doesn't depend on the others, so it can go last.
Two things to do while merging:
- Migration numbers. Every branch numbered its database changes from the same
starting point, so each PR after the first renumbers its migration to the next free
number when it's merged.
dev/pilothas already done this (numbers 0016–0019), so we know it works. - Three merge fixes must be re-made. They exist only on
dev/pilottoday. With the suggested order, PR 2 (photos) carries fix 1, PR 3 (video) carries fixes 2 and 3, and PR 4 (consent) carries none. They are listed in thedev/pilotsection below.
PR 1 — Moderation before the pilot, plus care referrals
In one line: everything a student writes or uploads is checked before anyone else sees it; the legal safety duties are built; and a reviewer can refer a student at risk to a named person at their school.
What we built
| Module | What it does | Where the code lives |
|---|---|---|
| Tougher text check | Catches disguised writing: numbers for letters, look-alike characters, a phone number with spaces in it. Guards the AI check against "ignore your instructions" tricks. | API moderation/normalize.ts, stage1.ts |
| Nothing shown before it's cleared | Posts, comments, messages and profile text wait until they pass. If content is deleted during review, its review closes too. A decision is saved completely or not at all, so nothing is left half-approved. | API moderation/service.ts, feed, messaging |
| Smarter AI check | The AI sees a short excerpt of the conversation, with names replaced by roles. Every decision records which version of the rules it used. | API moderation/service.ts, classifier*.ts |
| One review list, worst first | Four lanes: at-risk students, child safety, held messages, everything else. A student who keeps trying to sneak past the filter has everything new they post sent to a person for 24 hours. | API moderation/routes.ts; web ModerationPage, priority.ts, QueueCard |
| Report and remove | Profile text and school descriptions can be reported. A reviewer can remove a live post, comment, profile photo or banner for breaking the rules, without waiting for a report. | API moderation/*; web RemoveDialog, ReportDialog |
| Vendor test kit | Runs the same test set through any AI checker, so suppliers are compared fairly (the "bake-off"). | API scripts/moderation-eval.ts |
| The legal safety layer | Five parts, listed below the table. | API safety/*, lib/phash.ts; web SafetyPages, SafetyPanels |
| Care referrals (D-67) | A reviewer refers a student at risk to a named care contact at their school. The school must reply within 24 hours (1 hour if urgent). An overdue referral tells our team to phone the school. The school gets a Student support page. Thnkrr never contacts parents. | API care/*; web ReferDialog, CarePanel, StudentSupport, CareCard |
| Test stub made safe | The laptop stand-in that approves everything never approves self-harm language. | API classifier-dev.ts |
The legal safety layer, in five parts:
- A public page to ask for an intimate image to be taken down. India's law gives us 2 hours.
- Safety cases, with the evidence kept for a year.
- A block list, so a removed image can't be uploaded again.
- Complaints, with India's deadlines: acknowledge within 24 hours, and deal with a removal request within 72 hours.
- A helpline card for each country: 988 in the US, Tele-MANAS 14416 in India.
New database tables: safety cases, removal requests, the media block list, complaints (grievances) and care referrals.
Docs:
- New: docs/20 (the research and the recommended checking setup), docs/21 (the founder's one-page decision brief) and docs/22 (the safety runbook).
- Updated: docs/15 and docs/17.
- 29 September: all three research docs gained video and local-model costs.
Stubs and stand-ins left
| Switch | What it does today | Before going live |
|---|---|---|
MODERATION_DEV_AUTOAPPROVE | On a laptop with no AI key, approves everything except self-harm language. Forced off in production. | Run the bake-off, then pick the real AI checker. Today our code can only talk to Claude. The two recommended options — OpenAI's free moderation check, and the gpt-oss-safeguard model rented through Amazon (Bedrock) — still need that link written. |
SAFETY_HANDLER_IDS | Empty, so any moderator can open a safety case. | Set it to the two named people before the first real case. |
| Reporting to NCMEC (the US National Center for Missing & Exploited Children) and the Indian police | The case record and the runbook (docs/22) are built. A named person files the report by hand. | We aren't registered with NCMEC yet. The founder names the filer. |
| Block list | Holds only images we removed ourselves. | Connect the free child-abuse lists after the applications are approved. |
Not built (known, planned)
- The escalating penalty ladder for repeat rule-breakers (docs/15 step 11).
- A check that looks across a whole conversation between two people for risk (docs/15 step 10).
- Pages in Hindi. Held back until after the pilot, by the founder's call on 25 September.
Known issues
- Three out-of-date browser tests. Commit
199f58brenamed the review screen, and three tests still look for the old names. The product is correct; the tests need updating before the PR.
Last test run (28 Sept, on this branch): 341 of 341 API tests passed.
PR 2 — Photos in posts (D-66)
In one line: a post can carry photos, and it works like LinkedIn's.
What we built
| Module | What it does | Where |
|---|---|---|
| Photo upload and processing | Up to 8 photos per post, 8 MB each. Each photo is rebuilt to remove hidden location data, and resized. | API media/service.ts, lib/storage.ts |
| Photos in the feed | A grid on the post, and a full-size viewer. Each photo has alt text for screen readers. | API feed/service.ts; web PostImages.tsx |
| Writing a photo post | Pick photos, add alt text to each, then post. | Web PostEditor.tsx |
| A person checks every photo post | The AI check can only read words, so a post with photos always goes to a person, even if its words pass. | API moderation/service.ts; web QueueCard |
| Removal deletes the files | Removing or deleting the post deletes the public copies of its photos. | API media/service.ts |
New database table: post images.
Stubs and stand-ins left
- On a laptop,
MODERATION_DEV_AUTOAPPROVEalso approves photos straight away. Forced off in production. - No automatic photo check yet. A person sees every photo. The Amazon picture check is recommended (docs/20 §10) but not built.
- No child-abuse matching yet. It waits on the free applications.
Ships
The whole feature. It is complete and pushed.
Last test run: not re-run on its own since it was built (26 Sept). Its tests ran
inside the full dev/pilot run on 28 Sept, and all passed.
PR 3 — Video in posts (D-69)
In one line: a post can carry one video, with LinkedIn's limits (15 minutes, 5 GB). It's captioned automatically, and a person watches every video before it's shown.
What we built
| Module | What it does | Where |
|---|---|---|
| Upload in pieces | Big files go up in 16 MB pieces, three at a time. If the connection drops, the upload picks up where it stopped. | API video-service.ts, lib/storage.ts; web lib/video-upload.ts |
| Processing on our own server | Every video is rebuilt, and hidden location and phone details are removed. The server makes a smaller copy for the feed (480p) and a sharper one (720p) for full screen, plus a poster and a 30-frame storyboard. The file is read with strict limits, so a booby-trapped video can't harm the server. | API media/video.ts |
| Automatic captions | Speech is turned into text on our own server (Whisper), so no student's voice leaves our systems. The student checks the captions line by line, uploads their own caption file, or chooses none. | API lib/transcriber.ts; web ComposerVideo.tsx |
| Checking what a video says | The spoken words and the captions go through the same text check as a post. A phone number said out loud flags the post. | API moderation/service.ts |
| Watching | In the feed, a video plays silently as you scroll to it, with captions on. Tap it for full size, with sound and a speed control. Autoplay can be switched off in Settings. No view counts. | Web PostVideo.tsx, AppearanceSection.tsx |
| Reviewing | The reviewer sees the storyboard first. The video stays blurred and silent until they choose to play it. Approve stays locked until the whole video has been played (up to 2× speed; skipping ahead doesn't count). | Web ReviewVideo.tsx, QueueCard |
| Removal deletes the files | Removing or deleting the post deletes every public copy. | API video-service.ts |
Database changes: no new table. The existing media and posts tables gain video columns (length, sound, captions, transcript, and a link from each post to its video).
Docs:
- Updated: docs/07, 08 and 13.
- New on 29 September: docs/23 (the full video record: storage, checks and costs) and docs/24 (the founder's one-page summary).
- Also updated on 29 September: docs/18 and 19 (Amazon costs and alternatives) now include video.
Stubs and stand-ins left
WHISPER_MODEL(the path to the speech model file). If it's empty, there are no automatic captions and nothing said in a video is checked automatically; the reviewer's own listening is the only check. It is set ondev/pilot. The live server needs the speech program and its 148 MB model installed.- Video length is a setting (
VIDEO_MAX_DURATION_S). Today it is 15 minutes; the founder may shorten it. - On a laptop, the auto-approve stub also approves videos. Forced off in production.
Not built (the full list is in docs/23 §7)
- Child-abuse matching for video. Free, but it needs three applications (Project Arachnid, Google CSAI Match, NCMEC).
- The automatic picture check on video frames. About $54 a month with Amazon, or $0 on our own server (nudity only).
- Two fixes that need moderation and video merged first:
- The intimate-image takedown tool must learn to remove video files.
- A removed video must go on the block list, so it can't be uploaded again.
- The "that's me and I didn't agree" request for videos, and a one-page checklist for reviewers.
- Live-server setup: install ffmpeg and Whisper; set storage rules (see "Before anything goes live" below); give video its own work queue. Today, photos wait behind a long video.
Known issues
None open. The walkthrough on 28 September found four bugs in the video players, all fixed and re-checked in the browser:
- the review timer undercounted;
- the full-size player sometimes wouldn't start;
- the feed player sometimes wouldn't start;
- the feed copy kept playing behind the full-size one.
Last test run (28 Sept, on this branch): 281 of 281 API tests passed, including a real video run through ffmpeg.
PR 4 — Parental consent (D-68)
In one line: a student's account opens only after their school confirms who they are and a parent or guardian deliberately says yes.
What we built
| Module | What it does | Where |
|---|---|---|
| School check first | The student names a guardian. A named person at the school (the "consent verifier", chosen by our team) checks those details before any email goes out. | API consent/service.ts; web SchoolStep, GuardianChecks |
| The guardian's page | Nothing happens just by opening the link. To say yes, the guardian ticks a box, answers yes or no on mentors separately, and types their name. A paper form is available as another route. | API consent/*; web ConsentPages.tsx |
| Withdrawal | The confirmation email carries a link that lets the guardian withdraw consent at any time. Withdrawing pauses the account. | API consent/*; web ConsentPages.tsx |
| Clocks and reminders | The guardian's link lasts 7 days. If the school hasn't checked the guardian's details after 2 school days, it gets a reminder; after 5 school days, our team is told. A sign-up nobody said yes to is closed and deleted after 30 days. All of these are settings. | API consent/service.ts, hourly job |
| Waiting screen = profile form | While they wait, the student fills in their profile, which saves as they type (on the device only). A status panel sits on top. This was the founder's redline. | Web ProfileStep, lib/profile-draft.ts |
| Mentor rule | Only a guardian's explicit no blocks mentor requests. | API mentorship/service.ts |
| Private by default | Guardian contact details are encrypted in the database. Every step is recorded as evidence (the consent events table). | API lib/crypto.ts, consent_events |
| India's adult check | A connector for checking that the guardian is an adult (India's rule, from May 2027). Today it only has a test version. DigiLocker (the Indian government's digital documents app) plugs in later. | API consent/adult-check.ts |
Docs: updated docs/14 and docs/16 (the founder's redline folded in).
Test data: Mr. Okafor is North Shore Academy's consent verifier and care contact. This fixed the "North Shore has no consent verifier" email.
Stubs and stand-ins left
| Switch | What it does today | Before going live |
|---|---|---|
CONSENT_DEMO_AUTOCONFIRM | On a laptop, consent confirms itself after 3 seconds. It can never run in production. It is off on dev/pilot, so the real flow can be tested. | Nothing; production ignores it |
ADULT_CHECK_PROVIDER | Only a test version (dev) exists. | DigiLocker, before India's rule starts (May 2027) |
DATA_ENCRYPTION_KEY | On a laptop it's derived from another secret. | Required in production; the server won't start without it |
OPS_EMAIL | Empty, so alerts meant for our team are only written to the log. | Set it to the team inbox |
SUPPORT_EMAIL | A placeholder address (support@thnkrr.local). | The real support address |
A choice to confirm
A student whose school isn't on Thnkrr has their sign-up closed. This used to apply only to under-18s; it now applies to every student. We flagged it at the time, and it is easy to reverse.
Last test run: after the final fix, the tests for that fix were re-run (48 of 48
passed). The full dev/pilot run later on 28 Sept included this fix, and all passed.
dev/pilot — all four together
What it is: a local branch that merges all four features, so they can be tested
together on localhost. It has its own database and file storage, separate from main's.
It never becomes a PR.
Rule: fixes are made on the feature branch, then merged into dev/pilot again.
The only changes made directly here are the fixes needed where two features meet.
Migration numbers here: 0016 photos, 0017 care referrals, 0018 consent, 0019 video.
The three fixes that exist only here. Each must be re-made in the PR named in brackets (with the suggested merge order):
- Photos + moderation (API) [PR 2]: the "a photo post always goes to a person"
rule, moved into moderation's rewritten review code. Plus one line in a test
(
images: []). - Video + moderation (API) [PR 3]: the same rule for video posts. The AI only reads the words and speech, never the pictures.
- Video + care (web) [PR 3]: on a video post flagged for self-harm, the reviewer's Publish button also stays locked until the video has been watched in full.
Last full test run (28 Sept):
- API: 374 of 374 passed.
- Browser tests: 110 passed and 3 failed. The 3 are the out-of-date moderation tests (PR 1, Known issues).
How to run it: stop the main stack first, because both use the same ports. Then
start the API in thnkrr-api-combined and the web app in thnkrr-web-combined. The
.env there has the speech model set and the consent stub off.
Before anything goes live (all four together)
| Area | What's needed |
|---|---|
| Settings | Real AI checker chosen and its key set; SAFETY_HANDLER_IDS; DATA_ENCRYPTION_KEY; OPS_EMAIL; SUPPORT_EMAIL; WHISPER_MODEL |
| Server | Install ffmpeg, the Whisper speech program and its model file |
| File storage rules | Let the web app read caption files and see each upload piece's receipt (the ETag header). Delete half-finished uploads after 1 day. Delete original videos after 30 days, except those kept for a safety case. |
| Amazon account | Switch on Amazon's opt-out that stops it keeping or using our pictures to improve its AI, before Amazon checks the first student photo or video still |
| People | Four named people: two reviewers, who also handle safety cases (SAFETY_HANDLER_IDS); the person who files NCMEC reports; and the Grievance Officer. Someone to cover about 18–25 minutes of video review a day. A consent verifier and a care contact at each pilot school. |
| Outside applications | The free child-abuse lists: Microsoft's PhotoDNA, the UK's Internet Watch Foundation (IWF), Canada's Project Arachnid and the US NCMEC; for video, also Google's CSAI Match. Plus registering with NCMEC so we can file reports, and a quote from Thorn (a child-safety non-profit) — or Google's free Content Safety API instead. |
| Tests | Update the 3 out-of-date browser tests |
What we need from the founder
Everything is set out in two one-page briefs:
- docs/21 (moderation):
- sign off the recommended checking setup;
- send the two supplier emails;
- name four people (two reviewers, the NCMEC filer, the Grievance Officer);
- approve the photo checks.
- docs/24 (video):
- the video length for the pilot;
- the picture check: Amazon or our own server;
- whether to delete original videos after 30 days;
- the free applications;
- the reviewer hours.
- One more, from PR 4 (consent): confirm that a student whose school isn't on Thnkrr has their sign-up closed at every age. It used to apply to under-18s only.
New or updated today (29 September)
| Doc | Branch | What changed |
|---|---|---|
| docs/23 — Video: storage, checks and costs | video | New. The full record. |
| docs/24 — Video: one-page summary | video | New. For the founder. |
| docs/18 — Amazon services and costs | video | Video storage, delivery, processing and checks added. The Lightsail plan choice. |
| docs/19 — Why Amazon, and the alternatives | video | Why video is processed on our own server, not a video service. Speech-to-text on our server vs Amazon Transcribe. What running models on our own server costs. |
| docs/20 — Moderation research | moderation | New §11 on video. Computing cost of running a picture model on our own server. |
| docs/21 — Moderation decision brief | moderation | Video row, video costs, and one more thing we need from the founder. |
| docs/15 — Text moderation | moderation | A note on which models can run on our own server, and what they cost. |