From CSV to signed certificate.
The full operator handbook for Meridian/V — the ingest schema we accept today, every rule the engine enforces, who can do what, what a certificate actually proves, and how a third party verifies one.
What Meridian/V is — and what it isn't.
Meridian/V is a validation and certification workflow for road-asset survey data. An engineer uploads a CSV; the platform runs a deterministic rule pass; a second engineer co-signs; a SHA-256-hashed certificate is issued and is independently verifiable. That's the whole loop.
- A CSV ingest aligned with the TMH-18 v4 exchange format.
- A deterministic, auditable rule engine.
- An evidence chain: every override carries a justification; every certificate carries a co-signer.
- A public, no-auth verifier so receiving authorities can trust what they receive.
- Not a pavement-management system. Meridian/V validates and signs; downstream tools model life and budget.
- Not an FWD acquisition tool. Bring your own field data.
- Not a regulator. We align with TMH-18; the standard owners remain COTO and the Department of Transport.
- Not a substitute for engineering judgement — the co-sign exists precisely because rules cannot capture every case.
Who uses the platform
Three audiences, with different jobs-to-be-done:
| Audience | Typical job | Lands on |
|---|---|---|
| Road authorities (SANRAL, provincial DoTs) | Receive verified pavement-condition data from contractors and consultants. | /verify + dashboard |
| Consulting engineers | Upload, review, and request co-sign on data they prepared on behalf of an authority. | Submission cockpit |
| Contractors / FWD operators | Hand raw survey output to the consulting engineer for review. | Submission cockpit (engineer role) |
Five steps, end-to-end.
The full submission lifecycle through the web UI. Programmatic equivalents are tracked in the API section.
1. Sign in
Access is by application during pre-pilot. Companies apply at /sign-up; on approval the primary contact receives a one-time magic link to set their admin password and can then invite the rest of the team. Magic links carry a 24-hour TTL — if it expires, request a fresh invite from your org admin.
2. Upload a submission
Go to Submissions → New submission, give the dataset a title, attach the CSV, and click Upload & validate. File requirements:
- UTF-8 encoded (no BOM preferred)
- ≤ 50 MB
- Required headers (case-insensitive):
ROAD_NUMBER, SECTION_ID, CHAINAGE, DATE, D0, D200 - One SECTION_ID per submission for cleanest results (see Troubleshooting)
Full reference: CSV format & headers ↓.
3. Review findings
The validation engine runs synchronously on upload (typical: under 2 seconds for files under 10 MB). The cockpit shows a finding card per triggered rule, each with severity, internal rule reference, the human message, and the affected row count. ERR findings block co-sign; WRN findings can be overridden with a written justification — every override is timestamped and pinned to your user record in the audit log.
4. Co-sign & certify
When the dataset is clean (or all warnings carry an override), click Request co-sign. A user with the principal_engineer role in your org receives a notification and can approve or reject. On approval, a SHA-256 hash is computed over the stored file bytes and a certificate ID is published. A submitter cannot co-sign their own work — the platform enforces separation of duty at the role layer.
5. Verify
Anyone — no login required — verifies a certificate at /verify. The verifier returns the dataset metadata, signers, issued time, and SHA-256 hash so a third party can re-hash their copy of the file and confirm a match. See the Public verifier section for the exact procedure.
States, gates, and who acts.
Every submission moves through a finite state machine. The states are visible in the audit log and on the submission detail page; transitions are append-only.
| State | Meaning | Next action |
|---|---|---|
| DRAFT Draft | File uploaded; row 0 of validation has run and basic format checks passed. | Engineer reviews findings and either fixes the source file (and re-uploads as a new submission) or proceeds. |
| VALIDATED Validated | All ERR-severity findings are clear; any WRN findings have either been resolved or carry an override justification. | Engineer requests co-sign. |
| AWAITING_COSIGN Awaiting co-sign | A request has been sent to the org's principal_engineer pool. Email notification dispatched. | A principal_engineer (other than the submitting engineer) approves or rejects. |
| CERTIFIED Certified | Co-signature recorded. SHA-256 computed over stored file bytes. Certificate ID issued and published to the verifier. | Optional — share the certificate ID with the receiving authority. |
| WITHDRAWN Withdrawn | A principal_engineer has revoked a previously-issued certificate. The certificate row remains queryable; the verifier marks it WITHDRAWN and exposes the reason. | Re-submit a corrected dataset as a new submission. |
| REJECTED Rejected | Co-sign was declined. The submission stays editable so the engineer can address the technical concern. | Engineer addresses the rejection note and re-requests co-sign. |
Typical timing
- Upload → DRAFT: sub-second to 2 seconds for a 10 MB file.
- DRAFT → VALIDATED: as long as it takes the engineer to review findings.
- VALIDATED → AWAITING_COSIGN: instant on click; an email is dispatched within a minute.
- AWAITING_COSIGN → CERTIFIED: bound by your principal engineer's response time, not platform latency.
The exact ingest schema.
Meridian/V's current ingest is a strict subset of TMH-18 v4. Required columns must be present (case-insensitive). Recognised optional columns are stored verbatim and surfaced in findings; unknown columns are preserved in the file but not interpreted.
Required headers
| Header | Type | Unit | Description |
|---|---|---|---|
| ROAD_NUMBER | string | — | National or provincial road designation, e.g. N1, R21, M3. |
| SECTION_ID | string | — | Authority section identifier the chainage is measured against. |
| CHAINAGE | decimal | km | Distance from section start. Must be strictly increasing within a section. |
| DATE | ISO-8601 date | YYYY-MM-DD | Survey date for the row. UTC, no time component required. |
| D0 | decimal | µm | Centre deflection under the standard 40 kN load, temperature-uncorrected. |
| D200 | decimal | µm | Deflection at the 200 mm offset sensor, used in the D0/D200 sanity ratio. |
Recognised optional headers
| Header | Type | Unit | Description |
|---|---|---|---|
| D300, D450, D600, D900 | decimal | µm | Additional FWD geophone offsets. Recognised by the parser; not yet rule-checked. |
| LOAD_KN | decimal | kN | Applied falling-weight load. Typically 40 kN; recorded for traceability. |
| TEMP_AIR | decimal | °C | Ambient air temperature at measurement time. |
| TEMP_SURFACE | decimal | °C | Pavement surface temperature at measurement time. |
| TEMP_CORRECTION | decimal | multiplier | Per-row temperature correction coefficient. If missing, the engine raises the TEMP-CORRECTION warning. |
| LANE | string | — | Lane code (e.g. L1, L2). Free-text in current ingest. |
| DIRECTION | string | N/S/E/W | Travel direction at chainage. |
| OPERATOR_ID | string | — | Operator or crew identifier for traceability. |
Example file
A minimal valid file (truncated). The full sample is shipped in the repo as sample-tmh18.csv:
Encoding & line endings
- Encoding: UTF-8. UTF-16, Windows-1252, and Latin-1 are rejected with the ENCODING-UTF8 error.
- Line endings: LF or CRLF, both accepted.
- Delimiter: comma. Semicolon-delimited exports (common in continental tooling) are not currently supported.
- Quoting: RFC 4180. Embed commas inside double-quoted fields; escape internal quotes by doubling.
- Empty cells: empty string, not
NULLorNaN. The optional-column rules treat empty as absent.
Every rule, every fix.
The engine runs five rules on every upload. Internal rule references are stable identifiers — you can cite them in support emails or audit reports. Rules will be added as additional TMH-18 sections are implemented; once added, a rule reference is never reused for a different check.
When it fires
The uploaded file contains the Unicode replacement character (U+FFFD), indicating it was not stored as valid UTF-8 — typically Windows-1252 / Latin-1 saved without conversion.
Why it matters
TMH-18 exchange files are required to be UTF-8 so that section labels containing the § sigil and other authority-specific glyphs are not silently corrupted between systems.
How to fix
Re-export from the survey software with UTF-8 encoding, or open the file in a modern editor (VS Code, Notepad++) and "Save As → UTF-8" before re-uploading.
When it fires
One or more of the required headers (ROAD_NUMBER, SECTION_ID, CHAINAGE, DATE, D0, D200) is missing from row 1.
Why it matters
Without the canonical header set, the engine cannot map column positions to schema fields and validation cannot proceed.
How to fix
Add the missing column(s) to the header row. Header names are case-insensitive and trimmed, so MR_NUMBER vs Road_Number is fine — but the underlying token must match.
When it fires
A row's CHAINAGE value is less than or equal to the previous parsed value, suggesting a section was traversed in the wrong direction or rows were re-ordered.
Why it matters
Chainage is the spatial primary key for road-asset data. Non-monotonic chainage typically indicates a directional flip, a duplicate row, or a malformed section join, all of which break downstream segment aggregation.
How to fix
Sort rows by SECTION_ID then CHAINAGE before export. If you intentionally surveyed in reverse direction, post-process the chainage to be increasing along the SECTION_ID frame of reference.
When it fires
For one or more rows, D0 / D200 falls outside the [1.0, 8.0] sanity range.
Why it matters
A ratio below 1.0 is physically implausible (the offset deflection cannot exceed the centre deflection on a uniform pavement); above 8.0 typically indicates a sensor calibration drift, an underlying bedrock anomaly, or a transcription error rather than real pavement behaviour.
How to fix
Inspect the flagged rows. If real, attach a justification when overriding (e.g. known geotechnical anomaly at the chainage). Otherwise, re-run the FWD with calibration verified.
When it fires
The TEMP_CORRECTION column is missing entirely, or one or more rows have an empty TEMP_CORRECTION cell.
Why it matters
Deflection values are temperature-sensitive. Without a per-row correction coefficient, downstream pavement-life models become unreliable across surveys collected in different seasons.
How to fix
Add a TEMP_CORRECTION column derived from your standard correction model (or the authority-supplied lookup) for every row. Where temperature data is intentionally not collected, override with a justification noting the survey context.
Severity semantics
| Code | Meaning | Effect on co-sign | Override |
|---|---|---|---|
| ERR | Schema or structural violation. The data cannot be trusted. | Hard block. | Not permitted. |
| WRN | Plausibility / completeness concern. The data may still be valid in context. | Soft block — clears with override. | Permitted with written justification. |
Rules on the roadmap
The following are tracked but not yet shipped. Order is indicative, not committed.
- SECTION-CONTINUITY — chainage continuity across SECTION_ID joins, not just within a single section.
- DATE-WINDOW — DATE column must fall within an authority-supplied survey window per submission.
- LOAD-TOLERANCE — LOAD_KN within ± 5% of declared nominal (40 kN by default).
- SENSOR-COMPLETENESS — at least four of D0, D200, D300, D450, D600, D900 populated per row.
- OPERATOR-CONTINUITY — single OPERATOR_ID per (SECTION_ID, DATE) combination unless explicitly noted.
- VISUAL-MATCH — when a TMH-9 visual assessment file is co-submitted, cross-reference distress codes.
Who can do what.
Permissions are role-based at the org level. A user has exactly one role at a time within an org. Cross-org access is reserved for super_admin, and even then only for platform-operations tasks, not customer data inspection.
- Invite and remove users
- Change user roles within the org
- View and configure org settings
- Submit datasets
- Withdraw a submission before co-sign
- Co-sign a certificate (separation of duty)
- View other organisations' data
- Upload submissions
- Review and override warnings with a justification
- Withdraw a submission they own before co-sign
- Request co-sign
- Approve their own co-sign request
- Invite users
- Modify another user's submission
- Receive co-sign requests
- Approve or reject a co-sign
- Annotate the certificate with technical notes
- Withdraw a certificate (with reason; produces a public revocation record)
- Co-sign a submission they themselves uploaded
- Modify the underlying file bytes once uploaded
- Approve or reject incoming organisation applications
- Suspend an organisation
- View global audit log
- Promote a user to super_admin via the bootstrap script
- Co-sign on behalf of a customer org
- Read submission file bytes outside an explicit incident-response procedure
What a certificate proves.
A Meridian/V certificate is a tamper-evident attestation that at a specific time, two named engineers signed off on a specific dataset whose bytes hashed to a specific value. It is not a statement about the truth of the underlying pavement measurements — that responsibility remains with the engineers — but it is a strong statement about provenance and immutability.
What a certificate contains
- Certificate ID — 12-character base32, unique platform-wide.
- Dataset metadata — title, organisation, original filename, row count, byte length.
- Submitter identity — name and role at time of upload.
- Co-signer identity — name and role at time of approval.
- Override summary — count and rule references of any warning overrides applied.
- Issued-at timestamp — UTC, ISO-8601, set by the database at the moment the co-sign succeeds.
- SHA-256 hash — hex-encoded digest computed over the stored file bytes (not the original upload bytes — see below).
The hash
The hash is computed over the bytes of the file as stored in the platform's S3 object — not the bytes the browser uploaded. In practice these are identical (Meridian/V does not transcode), but the distinction matters: the certificate binds to what we have, which is what a third party will re-download to verify.
Immutability and withdrawal
Certificate fields are not editable after issue. If a defect is discovered, a principal_engineer can withdraw the certificate — this does not delete the record; instead the verifier surfaces aWITHDRAWN banner with the withdrawal reason and timestamp. Receiving authorities should treat a withdrawn certificate as untrustworthy and request a fresh submission.
What a certificate is not
- It is not a digital signature in the X.509 / PKCS#7 sense. There is no per-engineer signing key — yet. Adding ECDSA keys per principal_engineer is on the roadmap.
- It is not a guarantee that the underlying field measurements are accurate; it is a guarantee that the file you re-hash is byte-identical to the file two engineers signed off on.
- It is not a substitute for the authority's own acceptance procedures. Authorities still own the gate decisions on their road network.
How a third party checks a certificate.
The verifier at tmh18.com/verify is unauthenticated and read-only. Anyone with a certificate ID can confirm the binding. Three-step procedure:
- Open /verify, paste the certificate ID, submit.
- Note the SHA-256 hash and dataset metadata returned by the verifier.
- On your own machine, compute SHA-256 of the file copy you hold and compare. A bit-for-bit match means the file you have is the file the engineers signed off on.
Verifier response — exact fields
| Field | Example | Use |
|---|---|---|
| certificateId | K4Q9F2X7M3R8 | Sanity check — matches the ID you entered. |
| status | VALID | WITHDRAWN | Reject WITHDRAWN; treat the dataset as untrustworthy. |
| issuedAt | 2026-05-04T13:21:55Z | UTC ISO-8601 — when the co-sign was recorded. |
| organisation | Acme Pavement Consultants | Submitting org — confirm against the engagement letter. |
| submitter | D. Seyoum (engineer) | Who uploaded. |
| cosigner | P. Mthembu (principal_engineer) | Who approved. |
| filename | n1-section14-2026-04.csv | Original filename at upload time. |
| byteLength | 4 218 902 | Bytes — quick mismatch detector before re-hashing. |
| sha256 | e3b0c442…b855 | Compare against your local hash. |
| overrides | 1 × DEFLECTION-RATIO | Disclose what warnings were overridden, by whom, with what justification. |
What works today, what's coming.
Today: token issuance
Org admins can mint API tokens from Settings → Tokens. Tokens carry the role of the user that minted them, scoped to that user's org. The current scope is read-only on certificates and submissions — sufficient to wire up downstream BI / dashboard pulls.
POST-MVP: REST API
RoadmapA documented REST API for programmatic submissions, plus webhooks for cosign and certificate-issued events, ships after MVP. The committed surface area is:
| Method | Path | Purpose |
|---|---|---|
| POST | /api/v1/submissions | Multipart upload, returns submission ID + initial validation findings. |
| GET | /api/v1/submissions/:id | Submission state, findings, override history. |
| POST | /api/v1/submissions/:id/overrides | Apply a warning override with justification. |
| POST | /api/v1/submissions/:id/cosign-request | Move to AWAITING_COSIGN; emits notification. |
| GET | /api/v1/certificates/:id | Same fields as the public verifier, plus org-internal context. |
| GET | /api/v1/verify/:id | Public verifier (no auth) — JSON form of the /verify page. |
| POST | /api/v1/webhooks | Subscribe to cosign.requested, cosign.approved, certificate.issued, certificate.withdrawn events. |
POST-MVP: CLI
A small meridianv CLI is planned for CI integrations — submit a CSV, poll until certified, fail the build on ERR findings. It will wrap the REST API only; no separate auth flow.
How the platform handles your data.
Authentication
- User identity is managed by AWS Cognito (region us-east-1).
- New users are onboarded via 24-hour magic-link invites, not plaintext passwords.
- Sessions are short-lived JWTs; the secret is rotated on a documented schedule.
- Optional MFA (TOTP) ships before commercial launch.
Storage & encryption
- Submission files: AWS S3, server-side encryption (SSE-S3, AES-256). Versioning enabled.
- Metadata, audit log, certificates: AWS RDS PostgreSQL 16, encryption at rest enabled.
- In transit: TLS 1.2+ end-to-end. HSTS enforced on tmh18.com.
Data residency
Pre-pilot infrastructure is hosted in AWS us-east-1 (cost-driven). Migration to AWS af-south-1 (Cape Town) is on the roadmap before any commercial launch so customer data resides within South Africa, consistent with POPIA expectations. Pilot organisations are informed of this in writing at onboarding.
POPIA posture
- Lawful basis: contract performance with the submitting organisation.
- Data minimisation: the platform only collects user identifiers needed to operate the workflow (name, email, org, role). No location tracking, no behavioural profiling.
- Subject rights: deletion and export requests are routed to privacy@tmh18.com.
- Information Officer registration: pending operating-entity finalisation.
Email & anti-spoofing
- SPF:
v=spf1 include:amazonses.com -allon tmh18.com. - DKIM: 2048-bit signing, three CNAMEs published.
- DMARC: tightening from
p=nonetop=quarantineafter the first week of clean reports, thenp=reject. - Companion domain tmh18.app is locked down with SPF
-alland DMARCp=reject— no mail is sent from it.
Reporting a vulnerability
Report security issues to security@tmh18.com. We acknowledge within one business day and coordinate disclosure with the reporter. Please do not include exploit payloads in initial outreach; we'll arrange a secure channel.
Common stumbles, and the fix.
If your symptom isn't here, email support@tmh18.com with the submission ID and a screenshot of the findings cockpit. We respond within one business day during pilot.
Upload fails with "file too large".
Submissions are capped at 50 MB. Most field surveys exceeding this are multiple sections concatenated.
FIXSplit the export by SECTION_ID and submit one section per submission, or filter to the section the authority has actually requested.
"Missing required headers" with all six columns visibly present.
The header row contains a UTF-8 byte-order mark (BOM) glued to ROAD_NUMBER. Excel inserts this when saving as "CSV UTF-8".
FIXSave as plain CSV (no BOM) from your editor, or pre-strip with `sed -i $'1s/^\xEF\xBB\xBF//' file.csv`. The parser will tolerate this in a future release.
CHAINAGE-MONOTONIC fires on a clean ascending file.
A SECTION_ID change resets chainage to a lower value but the engine currently scans rows in file order, not grouped by section.
FIXUntil per-section grouping ships, submit one SECTION_ID per file, or interleave a small offset to keep chainage strictly increasing across the section boundary.
DEFLECTION-RATIO fires on every row.
Units are wrong — D0 / D200 are recorded in mm rather than µm, or the sensor offsets are reversed in the export.
FIXConfirm both columns are in micrometres (µm) and that D200 corresponds to the 200 mm-offset geophone, not centre.
Co-sign request never arrives.
No user in the organisation has the `principal_engineer` role, or the existing principal_engineer is the same user as the submitter.
FIXAn admin must promote at least one other user to principal_engineer. The platform enforces separation of duty: a submitter cannot self-sign.
Verifier returns "certificate not found" for a freshly-issued ID.
Certificate IDs are case-sensitive and the trailing dash variant is sometimes copied without the suffix.
FIXCopy the ID using the "copy" button in the certificate detail view rather than selecting text manually. IDs are 12 base32 characters.
Terms used in this manual.
- TMH-18
- Technical Methods for Highways manual #18, "Road Asset Data Electronic Exchange Formats", published by the Committee of Transport Officials (COTO) on behalf of the Department of Transport, South Africa. Defines the columnar exchange schema for road condition and pavement-survey data.
- COTO
- Committee of Transport Officials — inter-governmental committee that owns the TMH series.
- FWD
- Falling Weight Deflectometer — a trailer-mounted device that drops a calibrated weight onto a load plate and records pavement deflection at a row of geophones (D0 at centre, D200 / D300 / D450 / D600 / D900 at offsets in millimetres).
- Chainage
- Distance along a road from the section start, conventionally in kilometres. Acts as the spatial primary key in TMH-18 exchange files.
- Deflection ratio
- D0 / D200 — used as a quick-look indicator of pavement layer competence. Healthy ratios sit between 1 and 8; values outside trigger a sanity warning.
- Co-sign
- The Meridian/V workflow gate in which a principal_engineer (distinct from the submitter) approves a validated dataset, after which a SHA-256 signed certificate is issued.
- Certificate
- An immutable record consisting of: certificate ID, dataset metadata, submitter and co-signer identities, issued time, and a SHA-256 hash over the stored file bytes. Verifiable at /verify with no login.
- POPIA
- Protection of Personal Information Act, 2013 (South Africa). Establishes the lawful basis and conditions for processing personal information in South Africa, including data residency expectations.
- Audit log
- Append-only record of platform actions: uploads, overrides, role changes, co-sign requests/approvals/rejections, certificate withdrawals. Visible to org admins for their own org.
Where to ask.
Pilot orgs have direct email access to the team. We aim for a one-business-day first response; actual time-to-fix depends on the issue.
support@tmh18.com
Submission help, validation findings, account access, role changes.
security@tmh18.com
Vulnerability reports. Coordinated disclosure; one-business-day acknowledgement.
privacy@tmh18.com
Subject access, deletion, and export requests under POPIA.
accounts@tmh18.com
Pricing, contracting, post-pilot transition.
Last updated 2026-05-05. This page is regenerated whenever a rule, role, or workflow gate ships. Track changes alongside the platform release notes.