Verifying a document
A document RotorLab issues carries a verification code, and the SHA-256 fingerprint of every file issued under that code is kept on record. Anyone holding a copy can check it at rotorlab.app/verify without an account and without trusting whoever handed it over. The code says a document was issued; comparing the file says your copy is that document.
Your organization also keeps, with each code it issues, the record data the document printed, so you can show exactly what you handed over and when. That data is in your organization export, in issued_documents.json, it is counted under Issued documents in your records windows, and it is deleted with your organization's data. The verification page never shows it: it shows only the code's own facts.
Two kinds of download carry no code, and say so on the page: an operations manual's working copy (marked DRAFT, NOT APPROVED) and a crash report built from an analysis RotorLab did not make itself (see below).
Checking a copy you were given#
- Open rotorlab.app/verify, enter the Document code printed on the document and press Verify. The code is grouped as you type, so it can be entered with or without its dashes, in either case. A job's flight record and a client's flight statement carry a QR code and a link on page one that open this page with the code already filled in; scan or click it instead.
- The page shows Issued through RotorLab, then Issued for (the name of the organization the document was issued for), the document type, its title, when it was issued (in the issuing organization's time zone, as the document printed it), the content digest, and under Files issued under this code (SHA-256) the fingerprint, size and date of every file issued under the code. When the document has been independently timestamped, the page says when and by whom.
- Under Check the file you hold, choose the PDF or text file you were sent, or paste its SHA-256, and press Compare. A chosen file is fingerprinted in your browser and never uploaded; only its SHA-256 is sent.
The answer is one of three:
- This is the file that was issued: your copy is, byte for byte, a file issued under that code, for the organization named.
- Does not match: your copy is not any of the files issued under that code. It has been changed since it was issued, even by one character, or it was re-saved by another program. Do not rely on it; ask the sender for the file exactly as it was delivered.
- Not a file we issued: no document on record was issued as this exact file, or no document was issued under the code you entered.
A copy edited in any way (a changed date, a flight removed, "no go" turned into "go") no longer matches. A document that prints anything differently is issued under its own code, so an invoice before and after payment, or an occurrence report before and after it was filed, never share one. For an invoice, the page also shows its invoice number, purchase order and standing (outstanding, PAID or VOID), so accounts payable can tell which one it holds. A void invoice has a code of its own, and the codes given out before it was voided still verify as the invoice they were.
If you have the file but not the code, leave the code empty and compare the file: the page finds the code it was issued under and shows that record.
To paste a fingerprint instead of choosing the file, compute it with sha256sum file on Linux, shasum -a 256 file on a Mac, or certutil -hashfile file SHA256 on Windows. Case, spaces and a trailing file name are ignored.
A document with no file on record shows the code and the content digest only, and the page says so.
When the organization a document was issued for has since closed and its records were deleted, its code answers Issued; organization since closed, with the Document code, the Document type and the day it was Issued. That says a document was issued under the code on that date; its content and its file can no longer be compared on the page, so ask whoever gave it to you how it was kept.
Since it was issued#
A copy that verifies can still be out of date, so under Since it was issued the page says what has happened since:
- For a design dossier and a job's flight record, whether the records the document was drawn from read the same today as when it was issued. If they have changed, the page says the copy is a true copy of what was issued, not of the records today, and to ask the sender for a current one. If the record is no longer held, it says so. The comparison is redone at most every few minutes, and the page says it was made in the last few minutes.
- Otherwise, that this page does not compare this document with the records today. An occurrence report, a maintenance log and an audit binder print standing as it read on the day, so they are never compared, and a design dossier or a job's flight record that carries no record of what it was drawn from is not compared either.
- For a design dossier, a job's flight record, an occurrence report, a maintenance log and an audit binder, when a later document about the same record was issued, by date. Ask the sender for it. The page never gives the later document's code.
A document downloaded again#
Downloading a document again keeps its code: the same code covers each copy, and every file is recorded under it. The copy's verification block prints First issued, when the code was first issued, and This copy, when that copy was made, so a copy made after a request never reads as issued before it. The text versions' VERIFICATION block does the same.
Text documents#
The plain-text versions of a job's flight record, an authorization's operations report and an approved operations manual revision end with a VERIFICATION block: the document code, the content digest, when it was issued, and how to check it. They are checked exactly like a PDF. Downloading the same approved manual revision again keeps its code.
Audit packages and binders#
Each audit package and audit binder carries two files of its own beside manifest.json:
- HOW-TO-VERIFY.txt, written from what that package holds: who it was produced for and when, how many files the manifest lists, the steps below, the package's own code, and every document inside that carries a code of its own. For an audit package it also gives the recipe for the manifest's content digest.
- verify_package.py, a short checker that needs Python 3.8 or later and nothing else, and contacts nobody.
Run it on the zip, or on the folder it unpacked to:
python verify_package.py audit-package.zipIt recomputes the SHA-256 of every file and compares it with the manifest, naming any file that is missing, changed, or not listed. For an audit package it also recomputes the manifest's content digest, which must equal the content digest the verification page shows for the package's code. Given the zip, it prints the zip's own SHA-256. It then lists the documents with their own codes, says what it checked and what it did not (that RotorLab issued the codes, that the records are true, and the independent timestamps), and exits with status 0 only when every file matched.
For a package that carries the activity log, it also walks the organization's chain in activity_log.json. It recomputes each row's hash with the recipe activity_log_chain.json states and checks that each row follows the one before it, through the links the package gives for the organization's rows between the ones it carries. A row whose content does not match its hash, a missing link, or a row out of order is a FAIL naming the row's number in the chain. A row with no place in the chain is counted: one dated before the chain began gets a NOTE, because it was written before the chain existed and cannot be checked here, and one dated after it is a FAIL. In a package that withholds IP addresses, a row that recorded one carries ip_digest, the salted fingerprint the hash covers, so it is still checked from its content.
Then enter the package's code at the verification page and give it the zip: the zip's SHA-256 is kept under the package's code, so the page says whether it is the package that was produced. Check each coded document inside the same way.
The organization export#
The organization export is checked the same way. Its manifest.json lists every file in the archive with its size and SHA-256 and carries the export's own code; README.txt describes each file and its fields; and HOW-TO-VERIFY.txt and verify_package.py sit beside them. Run python verify_package.py on the zip, then enter the export's code at the verification page and give it the zip, whose SHA-256 is kept under the code. activity_log_chain.json holds the verdict on your organization's own activity log chain and the recipe to recompute it, and verify_package.py walks the chain in activity_log.json as it does for a package.
Crash reports#
RotorLab keeps its own analysis of an uploaded log for a day. A crash report downloaded in that time is built from that analysis and carries a code. It prints the log's SHA-256, its size and the identifiers the log carries, so a reader holding the log can confirm the report describes that log, and it prints a warning when the aircraft named on the report does not match the log. A crash report built from an analysis supplied with the request says "Analysis supplied by the requester; not verified by RotorLab." and carries no code. See Crash Analyzer.
Operations manuals#
Download and Download as PDF on a manual give its latest approved revision. The PDF prints the revision's content hash, and the verification page shows the same hash as the revision content hash. The working copy downloads separately as Draft PDF, marked DRAFT, NOT APPROVED, with no code. See Ops Manual.
What a document prints#
Every PDF RotorLab issues prints names, places, notes and units as they were typed in these scripts: Latin in every extension (a pilot called Zażółć, a site at Île Sainte-Hélène, a street in İstanbul), Greek (Αθήνα), Cyrillic (Москва), Armenian, Georgian, Hebrew, Arabic, Lao, Chinese and Japanese (東京, the kana included), and the technical symbols, so a pack's resistance prints as 12.0 mΩ/cell. The fonts are embedded in the document, so the file reads the same on any computer without a font installed, and searching and copying text works for every script it prints. Hebrew and Arabic are printed right to left, as written; Arabic letters print in their separate forms rather than joined. Korean (Hangul), Thai, the scripts of India (Devanagari, Bengali, Tamil and the others), Ethiopic and emoji are not printed: each such character prints as a question mark, and the rest of the line prints as typed.
Every PDF is tagged for screen readers: it states its language and title, and marks its headings, paragraphs, lists, tables with their header cells, and charts, diagrams and photographs with a text description, in reading order. Page furniture such as the running header, the footer and the page rules is marked as decoration, so a screen reader skips it. Tagging does not change what a document prints or how its code verifies.
What a code does not prove#
A code proves the copy is one issued through RotorLab, for the organization named, and when. It is a tamper-evidence check, not a digital signature, and carries no legal signature status. It does not prove the underlying records were correct, only that the document says what the records said at the time. For proof that records existed in their current form by a date, independent of RotorLab, see Independent timestamps.