The method
How a recording is specified, consented and made
A brief names the task, the environment and the device before anything is recorded. The Capture Partner consents on their own phone, and the receipt is written and verified against the ledger before the first byte of the Recording reaches us.
The brief
A capture brief is a row in one file, fourthuman_engine_api/config/capture_briefs.json. Eight briefs live there today. The API opens that file at import time and reads each brief's target volume and availability from it, and the build inlines the same document into the web bundle, so those two figures cannot disagree between the catalogue and the API. The wording of a title is not covered by that: the API keeps its own titles, and the two surfaces word one of them differently today, which the code says beside the entry that does it.
Each brief carries up to eleven fields and nothing else. brief_id names it. title, domain and tag say what the task family is and where it happens. device, resolution and spatial_tracks state the rig and what it must produce. tiers lists which annotation tiers apply. target_volume and availability describe what a commission would produce and whether we can take one. note is optional and carries a sentence a Commissioning lab should read; three of the eight have none.
The file declares its own provenance as target. Every target_volume is a number a commission would aim at rather than footage we hold, and the catalogue renders it behind a Target badge for exactly that reason.
Two things are settled outside the brief, and both matter before anyone plans against one. The stages a Recording passes through are set by the pipeline, and they are the same for every brief. The terms under which a delivery may be used are settled in the engagement.
For manipulation work, Training data for robotics sets out what a brief fixes and the formats a delivery can take.
The four availability values are defined once, in that file, and the definitions below are its own.
VALIDATEDrecords that the annotation pipeline has been run end to end on a real recording of that task family. Exactly one brief may carry it.PILOTrecords that a Capture Partner hub is being stood up for that domain, and that the brief is commissionable now.ON_DEMANDrecords that the brief is scoped and quoted per engagement.RESEARCHrecords that the brief is under evaluation and not open for commission.
The device
A brief's device field is a specification, agreed before anything is recorded. The eight briefs name six devices between them. Five are rigs — Ray-Ban Meta, GoPro Hero 12, a stereo global-shutter head rig, iPhone LiDAR and a simulator. The sixth is a human feedback portal on the RLHF brief, which records no footage at all; its resolution field reads that the video arrives from the Commissioning lab.
The one recording that has been annotated end to end measures 640×360 at 30 frames per second and runs 1,843 frames. That shape is the recording's own, not a brief's: it is the evidence behind the single VALIDATED badge, and it is evidence about the annotation pipeline rather than about any rig.
The device field is part of the brief's specification, and the engine does not read it.
What the system does record about a device is one identifier on the consent entry. The app generates it locally as DEV_PWA_ followed by twelve hexadecimal characters from six random bytes, stores it, and reuses it across sessions so the consent and the upload carry the same one. It identifies the installation rather than the handset: we store no make, no model, no operating-system version and no hardware fingerprint.
Consent, at the point of capture
The app moves a Capture Partner through four screens in a fixed order — the brief list, the brief, consent, then record. Consent is not a step at the end, and the screen states that in its own words: this is recorded before you record anything.
Four agreements sit on that screen, and they are separate rows because they are separate permissions. Three are required and one is not.
The button gates on the first, third and fourth, and deliberately not on the second, so declining training use stays a real choice rather than a dead end. An unticked optional box is sent to the server explicitly as false, and the ledger column is nullable with a null read as not granted, so silence never reads as agreement.
The receipt appears on screen the moment the entry is written. It carries the receipt id, the entry's position in the ledger, the timestamp, the entry's own SHA-256, and the hash of the entry before it. Each entry hashes its predecessor, and verify_chain() recomputes every hash and every link from the genesis value rather than trusting a stored flag, so a deleted, reordered or edited row is reported with the sequence number it broke at.
Fourthuman commits to processing a withdrawal request within seven business days of the asking. Business days are Monday to Friday, counted over IST calendar dates from the day the request was submitted. Public holidays count as business days, so a request reads older than it is, which errs early rather than late. Asking does not withdraw the consent by itself: it stays active and the footage stays where it is until the request is processed, and the withdrawal is then appended to the same ledger with its own timestamp.
What reaches a Commissioning lab from that entry is a reduced record. ConsentLedgerAuditRecord drops the partner identifier, the device id and the city, and keeps the receipt id, the task, the status, the timestamps, the policy text, the hash, the sequence and the previous hash. A buyer can check from that shape that consent exists, covers this task, is active and has not been silently edited, and none of those questions needs the name of the person who gave it.
No figure on this page is counted from the consent ledger: it is a local runtime file, so a count taken on one machine would not reproduce on another.
- The first agreement covers recording this brief for Fourthuman. There is no capture without it.
- The second agreement is optional and covers using the footage to train robot models. A Capture Partner can leave it unticked, record anyway and receive the same pay; the Clip is then refused from any training release, and
datasetReleaseService.create_releasereturns 422 naming the declined Clip. - The third agreement covers redaction and sound. It tells the partner that faces the detector finds are blurred automatically before footage reaches any buyer, that the copy a buyer receives carries no sound, and that the copy we keep retains it. How redaction runs: how faces are blurred.
- The fourth agreement states that withdrawal is a request a person processes, and not a switch that acts on its own.
The upload
The upload speaks tus 1.0.0, and it implements a subset of it: core, creation and termination. Creation-with-upload, expiration, parallel chunks and the checksum extension are not implemented. It does not answer tus's optional discovery request, so software written against the standard should not expect to negotiate extensions: every answer names the protocol version, and our own apps use only core, creation and termination.
Resumability is the design constraint here rather than a refinement. A Recording can run to several gigabytes, and on an Indian mobile network a connection that survives four gigabytes uninterrupted is the exception.
An older single-shot ingestion route is still mounted, and it reads the whole file into memory before a byte reaches disk. Its own docstring says it is retained for small clips and for the test harness and is not suitable for field capture, which is why the capture app uses the resumable route instead.
The offset is always the real size of the partial file on disk, read at request time, and never a counter that could disagree with the bytes we hold. That is what makes an interrupted transfer safe: whatever reached disk is the new offset, and the transfer continues from there without anything being reconciled.
The uploader is hand-rolled rather than a dependency, because the resume story is specific to us — the offset has to be reconciled against IndexedDB and against the server on every restart, which is a wrapper around any library we might have used. It sends each chunk over XHR, so a partner watching a three-gigabyte upload sees byte-level progress instead of a frozen bar. Chunks start at 8 MiB and halve down to a floor of 512 KiB after repeated failure, because retrying 8 MiB forever on a link that cannot hold it is how an upload never finishes. The server stays the authority on the offset: every resume begins with a HEAD, and a 409 mid-flight triggers a re-HEAD and a retry from the true offset.
The queue lives in IndexedDB, which stores the file itself alongside the upload URL and the byte offset reached so far. The work therefore survives a tab close, a browser kill and a phone reboot. localStorage would have made resumable mean resumable until you lock your screen.
Consent is checked more than once, because a multi-gigabyte transfer runs for hours. Creation refuses outright without a consent receipt id, and that id is verified against the ledger rather than trusted from the request. A transfer already in flight re-asks the ledger every 300 seconds, and the finaliser re-checks unconditionally before anything is minted. A withdrawal that lands mid-transfer stops the upload and discards the staged bytes rather than keeping them.
Several refusals happen before a byte moves. A non-video is rejected at creation rather than after four gigabytes. An upload longer than the 16 GiB Tus-Max-Size is rejected. A cohort code that names a place is rejected, and the refusal names the rule instead of saying invalid, because the cohort code reaches a filename that reaches a buyer.
Both halves are tested. test_resumable_upload.py reports 45 passed and 0 failed in process. test_resumable_upload_http.py reports 34 passed and 0 failed over HTTP. These two numbers are not transcribed from a terminal: a gate re-runs both suites before every push and refuses the push if either stops matching this sentence.
The per-upload lock holds within one server process. Integrity is checked once, on the finished file: the SHA-256 a Clip id carries is computed at mint, which catches a corrupted transfer at the end.
- Bytes that landed before a dropped connection are kept, and the offset equals the real bytes on disk rather than a counter.
- Another Capture Partner cannot append to this upload, terminate it, or even read its offset.
- An over-send is truncated at the declared length, and the file never grows past it.
- A completed upload cannot be terminated, and retrying its last chunk is a no-op rather than a 409.
- A withdrawal mid-transfer stops the upload, and the withdrawn bytes are discarded rather than kept.
- A ledger we cannot read fails loudly, and never as a withdrawal.
- An outage at our end does not destroy the partner's staged bytes.
What a Capture Partner sees
The app checks a recording on the phone before it uploads it. Rejecting a Clip after a four-gigabyte upload costs a partner an hour of mobile data; rejecting it ten seconds after recording, while the scene is still in front of them, costs neither of us and lets them shoot it again.
The check copies the server's method rather than approximating it. It samples frames, converts each to greyscale, and computes blur as the variance of the 3×3 Laplacian response and lux as the mean greyscale intensity, against the same thresholds the server's gate uses — 80.0 and 30.0, mirrored from one configuration and changed together. The phone samples twelve frames where the server samples up to fifty.
Laplacian variance depends on resolution, so the sampling never resamples. Frames are drawn at native pixel scale and a 3×3 grid of full-resolution tiles is read back. The grid is deliberate: egocentric footage is sharpest at the centre, where the hands and the action are, so a centre crop would flatter every clip and pass ones the server rejects.
The browser decodes video through its own colour path, whose matrix and range may differ from the server's. Because the two can differ, anything within 25% of a threshold reports as BORDERLINE rather than as a verdict.
Dropped frames are checked on the server rather than on the phone: the server detects them from decoder read failures, and a browser gives no equivalent signal.
preflight.test.mjs reports 27 passed and 0 failed, re-run 05-09-2026. Three of those assertions score a fixed sixteen-by-sixteen tile in the browser code and compare the result against numbers captured from the server's own algorithm in Python. Those numbers are a frozen reference written into the test file, so the three catch a change on the browser side. A fourth asserts that downscaling changes the sharpness score, which is what holds the measurement at native scale.
Beyond the check, a partner reads their consent receipt on the record screen, their consents with each one's current status in its own panel, and the device id that every consent entry carries.
A Capture Partner asks for consent to be withdrawn by emailing partners@fourthuman.ai, the address the consent screen gives. The sent email is the partner's own record of the date they asked: the engine has no route that returns a partner's Query to them.
Where the next stages pick up
Everything above describes code that runs today. The Capture Partner app is not served from this website. Cohort codes are sequence codes such as C01, and the one published reference Clip carries the reserved code DEMO.
- Redaction, and what happens when it cannot run: how faces are blurred.
- How a clip is judged, and how acceptance is reported: how a clip is judged.
- The one published reference Clip, annotated in full: the reference implementation.