Audit
The Audit page at https://rotorlab.app/audit is where a records request is answered. An inspector asks for one aircraft for one week; an insurer asks for twelve months; a records officer asks for one day. Three years of records and a one-day question should produce a one-day package, and this page produces it. It is the last page in the Records group of the side menu because it is where the other five end up.
Who may produce a package
Organization administrators, members holding the Auditor built-in role, and custom roles with Safety records: manage or Records custody: manage can produce packages and release copies, list them, and read the activity log. A role with Records custody: view reads the list of packages produced and the activity log without producing anything. Every member can open the page; a member without those rights sees a note to ask their administrator. Packages come with the Authorizations & Insurance add-on; the activity log is on the page on every plan, with or without it.
Request a package#
A package is a request: a period, a scope, who asked and why, and, for a records request, its number and the day its answer is due.
- Period: Today, 7 days, 30 days, 12 months, Everything, or any From and To dates. Empty bounds mean unbounded. The dates are your organization's days in its time zone, so a flight filed this evening falls inside "30 days" to today.
- Scope: the whole program, one aircraft, one pilot, one job, one occurrence, or one flight (see One flight).
- Form: Evidence package (every file, for an inspector or counsel) or Release copy (a readable flight list for a records request, see Release copy).
- Requested by and Purpose: who asked and what for. Requested by is required and is one line. These are recorded on the package and on the activity log.
- Request number, Received and Answer due: the number the request was logged under, the day it was received and the day the answer is due, all optional. Received cannot be later than today in your organization's time zone, and Answer due cannot be before Received.
- Withheld: anything kept back from the requester, each with the exemption you cite. Press Add a withheld item. In an evidence package the only kind is Other: say what was withheld (a video, a document kept outside RotorLab) and the exemption. A release copy also withholds a flight or one entry (below). Every item is kept on the list of packages produced, and the package's manifest lists each one with its exemption.
If any record cannot be right as it stands (a pack or a pilot on two flights at the same time, or a flight dated before its aircraft entered service), a card above the form says how many records need checking before a package goes out and links to the records check on the Pilot Logbook page. A package still goes out with the records as they stand.
Press Produce the package. The zip downloads. A request RotorLab cannot answer as given (an unknown column, an item with no exemption, a flight outside the period and scope) is refused with a message naming it, and nothing is produced. It leads with lists you can open in a spreadsheet, then the records themselves:
| File | What it holds |
|---|---|
register-flights.csv | One row per flight: the date, the aircraft, its registration, the pilot, the minutes flown, the job and client by name, the flight type, night, the standing at flight, the number of corrections and the remarks |
register-flights.pdf | The same flights as a document, with its own verification code: one row per flight and per person on its crew with their duty and time, and totals by aircraft, person, duty, mission and day or night (see Readiness reports) |
register-aircraft.csv and aircraft.json | The aircraft in scope (for a pilot, job or occurrence, the aircraft that flew its flights) with their airworthiness as of production and the reasons for it. In aircraft.json each aircraft carries its origin: its maker and countries and what your organization recorded on the covered foreign UAS lists, with where and when each was checked (see Origin and covered foreign UAS lists) |
register-people.csv and people.json | The people in scope (the pilots of its flights, the pilot asked about, or every member for the whole program) by name and role, with their credentials and currency as of production. No email addresses |
register-training.csv and training.json | The training sign-offs of those people, every date, with the evaluator, who recorded it, the evidence, the operations manual revision in force when it was signed and the date it is good until |
flights.json | Every flight in the period and scope, with its correction history, its legal-hold state, its stored-log metadata, the aircraft and pilot by name, its service record (the kind of work and every field as written, see Service record), and its standing: what the records said about its aircraft, pilot and authorization when it was filed (see Standing at the moment of flight), or not_stamped |
flights_voided.json | Every flight voided in the period and scope, whole, with who voided it, when and why, and, for a flight since restored, who restored it, when and why; a job package carries that job's voids and an occurrence package the void of its own flight |
maintenance_records.json, defects.json, work_orders.json, maintenance_program.json | The maintenance record for the aircraft in scope, calibrations included. For a pilot, job or occurrence, that is the aircraft that flew its flights. A one-flight package leaves these out: produce a package for the aircraft for its maintenance. A defect or work order opened before the period and still open during it is included and listed in the manifest under open_during_period, with why. The program is as it stands, not filtered by date |
maintenance-log-<registration>.pdf | The maintenance log document for each aircraft in scope (whole-program and single-aircraft requests), the same document Maintenance log PDF produces on the Maintenance page (with its Health alerts table when the aircraft has any Fleet Health alerts, and its origin and covered foreign UAS list record when one is recorded), as of production and covering every date, with its own verification code. A whole-program package renders the first 40 and lists the rest under omitted |
occurrences.json and occurrence-<ref>.pdf | Every occurrence in the period and scope, with every earlier version of the record, and its report as a document. An occurrence with no date, or one that happened before the period and was still open during it, is included too and flagged with the reason |
job_service_record.json | For a request about one job: the job's own service record, the kind of work and every field as written |
pilot.json | For a request about one pilot: their profile, credentials and currency as of production |
sms_hazards.json, sms_reviews.json, risk_assessments.json, authorizations.json, jobs.json, clients.json | For a whole-program request: the hazard register, the safety reviews and risk assessments of the period, the authorizations with their conditions, and the jobs and clients |
records_withdrawn.json | For a whole-program request: the records withdrawn in the period, each whole, with the reason, who and when (see Retention) |
legal_holds.json | The legal holds placed by matter that reach the records in this package, open or lifted |
disposals.json | The stored log files and precise positions in scope that were removed on the retention schedule or by a person, from the disposal register |
activity_log.json | For a whole-program request, the organization's activity log for the period. For one aircraft, pilot, job or occurrence, only the rows for the period that name a record in that scope (for one flight, every row about the flight or an occurrence about it, whatever its date), with no sign-ins and no IP addresses: a row that recorded an address carries ip_digest, the salted fingerprint its hash covers, which cannot be worked back to the address |
activity_log_chain.json | Whether your organization's activity log chain held across the rows in this package, the recipe for each row's hash, and the links between them |
attached_documents.json and register-attached-documents.csv | Every document attached to the records in the package (its occurrences, its maintenance records when they are included, the credentials and training sign-offs of the people in scope, and, for a whole-program request, the authorizations), each with the record it is filed on, the kind of record, its name, size in bytes, SHA-256 fingerprint and when it was attached. The documents themselves are not in the package: a reader who is handed a copy checks it against its fingerprint here. Both files are always present; with no documents attached, the list is empty |
00-audit-binder.pdf | For a whole-program request with no period: the audit binder as of production. A package with a period leaves the binder out, because the binder is the whole program as it stands, and the manifest says so |
manifest.json | The request itself (with its number, received and due dates), what was withheld with the exemption cited (withheld), the producer, every file's SHA-256 (with the document code of each PDF that has one), what was left out and why (omitted: maintenance logs past 40 aircraft, occurrence reports past 200, and any report that failed to render), the occurrences included from outside the period (included_outside_period), the work orders and defects open during the period (open_during_period), the open legal holds (legal_holds_open), the flights in the package flown outside standing with what was said and the reason given (flown_outside_standing), the basis of the hours of the flights in the package: how many came from flight logs held and how many were entered by hand, and how many flights were entered more than 24 hours after they flew (hours_basis), how many attached documents are listed (attached_documents), a note saying which files are filtered by date and which are current state, and the verification code |
timestamps/ | When records in the package have been independently timestamped: records.json with the proof of each of those records, the day files, the checker verify_anchor.py and a README with how each digest is computed. See Independent timestamps |
HOW-TO-VERIFY.txt and verify_package.py | How to check this package, written from what it holds, and an offline checker that needs only Python |
Each flight, maintenance record, defect, work order and occurrence in the package carries an anchor block: digest (the record's fingerprint by the published recipe), anchored (the day it was last independently timestamped, blank when it has not been yet), state (whether it still matches what was timestamped) and record (the record's anchored fields exactly as stored, which hash to digest; the row around it is shaped for reading, with names resolved, so it does not hash to the digest itself). That ties each record handed over to its proof under timestamps/.
The verification code in the manifest is issued over the manifest, and the SHA-256 of the zip itself is kept under the same code. A reader enters the code at rotorlab.app/verify and gives the page the zip: the page says whether it is, byte for byte, the package that was produced. Each PDF inside with its own code is checked the same way. See Verifying a document.
For an organization admin, or a role with Records custody: manage, Whole binder sits beside the button: the Audit Binder. An organization admin also sees Full export, the organization data export, which holds every record and your members' email addresses and stays with organization admins. Each takes the same Requested by and Purpose as a package (Requested by is required), downloads at once, and goes on the list below. The Auditor role sees neither; an Auditor's whole-program package already carries the binder.
One flight#
Choose One flight under Scope and type the flight's number in Flight number (the number the logbook shows, as in Flight #942). The package carries the whole flight, whatever the period you chose: its corrections, its stored-log details, its standing at the moment of flight, the aircraft that flew it, the occurrences that name it and every activity row about the flight or those occurrences, without IP addresses. A voided flight works too: the package carries it from the void ledger, with the occurrences it named when it was voided. The list of packages produced records the period as blank. On a filed flight in the Pilot Logbook, Audit package for this flight opens this page with the flight already chosen.
Release copy#
A release copy is what you hand a records requester: a readable list of the flights in the period and scope, with names instead of numbers and each flight's time as recorded, with the zone it was recorded in. Choose Release copy under Form. What the release copy carries lists every column it can hold, each with Release, Withhold or Leave out:
| Column | Released by default |
|---|---|
| Flight number, Date and time flown, Aircraft, Minutes flown, Flight type | Yes |
| Registration, Pilot, Site, Area (rounded), Night, Job, Call type, Requested by, Outcome, Imagery retained, Remarks | No |
No other part of a flight record can reach a release copy: no internal number but the flight's own, no sensor reading and no precise position. A column you leave out is not part of the release, and the copy names the columns it does not include. A column you withhold needs the exemption you cite, typed in the box under it. Under Withheld, A flight withholds one flight's whole row and One entry withholds one released column of one flight (it reads [withheld] in the copy); both take the flight number and the exemption, and the flight must be in the period and scope. Other records something kept outside RotorLab. Choose how the spreadsheet separates its cells under Spreadsheet, then press Produce the release copy.
The zip, named for the kind of request and the date (for example records-release-pilot-2026-10-01.zip), holds:
| File | What it holds |
|---|---|
records-release.pdf | The request (number, requester, received, answer due, period, scope, flights listed), the flights in the released columns, what was withheld and the exemption cited, and the columns not included, with its own verification code |
flights.csv | The same flights as a spreadsheet, the released columns only |
manifest.json | The request, the columns, how many flights are listed and how many voided flights are not, the withheld summary, every file's SHA-256 and the verification code |
HOW-TO-VERIFY.txt and verify_package.py | How to check the copy, and the same offline checker a package carries |
Withheld content is never in the copy or in what its code covers: the copy is built from what is released, so it carries its own code. The copy tells its reader what was withheld by column, and flights and entries by count and exemption, never by flight number. Its title names the pilot, the aircraft, the job, or the flight's number, date and aircraft only when that column is released with nothing in it withheld; otherwise it reads "One pilot", "One aircraft", "One job" or "One flight". The list of packages produced keeps every withheld item, flight numbers included, and the full scope.
Packages produced#
Everything the organization has handed out is listed, newest first: packages, release copies, binders and full exports, each with when, its Form (evidence package, release copy with the number of flights listed, audit binder or full export), the period (a binder or export reads "As it stood"), the scope, who asked and why, the Request (its number, received and due dates), what was Withheld (each item with its exemption, or "nothing"), who produced it, how many files, and its code. This is the list an organization shows an auditor who asks what it has handed out and to whom. Producing any of them also writes a line to the activity log, with who asked, the size and the SHA-256.
Each row has two links:
- Download again gives the file exactly as it left, byte for byte, so the copy you send today matches the one the auditor checked last month. A package or release copy downloads again for anyone who can produce one; a binder downloads again for organization administrators and roles with Records custody: manage. A full export is not kept: its row reads "Not kept: produce a new export", and only an organization admin produces a new one. Each download again writes a line to the activity log.
- Check the code opens the verification page with the code filled in.
Activity log#
The organization's activity log, oldest first within the period: sign-ins, permission changes, flights filed, corrected and voided, holds set and lifted, records released, occurrences amended and withdrawn, packages produced, organization exports and binders downloaded, CSV downloads of the log itself, everything RotorLab support opened or changed under support access, and every other event the application records, each with its level, category, actor and detail. Filter by date range, category or a search term; Show more pages through; Download CSV takes the filtered range as a spreadsheet, with each row's time written with your organization's offset (for example 2026-09-27T04:24:19-05:00), so the zone travels with the file. The selector beside the button chooses how the cells are separated: Comma-separated CSV, or Semicolon-separated CSV (Excel in Europe) for a computer whose spreadsheet writes decimals with a comma and otherwise puts a whole row in one cell. The choice is remembered in your browser and applies to every CSV download: the activity log, the organization export, public reporting, the registry import template, event winners and the Calibrate page's bench data. Each file starts with the UTF-8 byte-order mark, so Excel reads accented names as typed, and a cell that a spreadsheet would run as a formula is written with a leading apostrophe. The same log rides inside every audit package and inside the organization data export.
Besides sign-ins, the log records what changed on the records people edit, with the old and the new value: credentials added, changed and withdrawn, maintenance program tasks added, changed, completed and removed, dispatch sorties planned, changed, scrubbed and removed, jobs and clients, roles and their permissions and who holds them, member edits, safety register scores and mitigations, training program changes, manual section saves and reorders, and occurrence report downloads with their code. A withdrawal or deletion names what went, not only its number. A failed sign-in, a lockout or a failed two-factor or single sign-on attempt on one of your members' accounts appears in your log, named; the reply to the person signing in does not change. A successful change that wrote nothing more specific is still recorded as a records write naming the page action and the record.
Your organization's log is a hash chain of its own. Every row carries its number in your chain (chain_seq), the hash of your row before it (chain_prev) and its own hash (chain_hash), so a row edited or removed after the fact breaks every hash that follows. No other customer's rows are in your chain, so nothing another organization does, including closing its account, breaks your chain. activity_log_chain.json states the recipe each row's hash is made with, so anyone can recompute it with a few lines of code. In the organization export it says whether your whole chain held when the file was made (ok, how many rows it checked, the first row where it broke, break_at, if any). In a package it reports on the rows the package carries and gives the links (hashes only) of your rows between them, so a scoped package never speaks for rows it does not hold and never hands over rows outside the request. The verify_package.py in every package walks the chain itself (see Verifying a document).
Rows recorded before your organization's chain began have no place in it: they are counted as before_chain, and their content is still in the file. A row with no place in the chain that is dated after the chain began is counted as not_in_chain and reported as a break. Each day, the newest row of your chain is included in the independent timestamps when your organization has new rows.
What "voided" and "hold" mean#
Nothing on the record is destroyed, and no flight, maintenance or other record is deleted on a timer. Exactly two things are removed on a schedule, and your organization sets both on the Retention page: stored raw log files are deleted after the log window, and a flight's precise position (with its take-off and landing points, the position in its stored analysis, its correction history and its void-ledger copy) is reduced to the rounded area after the position window. Each is 90 days unless you change it, up to ten years, or kept for the life of the record, which is the default for a public body. Every pass writes one line to the activity log with the counts, the dates and your records schedule reference. Anything on legal hold keeps both, and each removal is listed in the disposal register. A flight is voided, not deleted: it leaves the live record and every total, and lands whole in the void ledger with who voided it, when and why; its stored log stays with it. Every correction to a flight is kept field by field with the person who made it. A legal hold on a flight stops the stored-log retention sweep and the position coarsening for that flight and stops it being voided; it is set automatically the moment an occurrence names the flight, or by hand with Place legal hold on the flight form by an administrator or a role with Safety records manage or Records custody manage, and lifted only with Lift hold and a reason; the flight's history keeps every reason (see Legal hold). While it stands, nothing the flight carries can be deleted: its stored logs, its checklist runs, and the documents and photos on the occurrence that named it. A legal hold can also be placed by matter on the Retention page, covering an aircraft, a pilot, a job, a span of dates or every record. A package's flights.json carries each flight's hold, and the manifest's on_legal_hold lists the held flights in the package with the date and reason, naming the matter of a hold placed by matter. An occurrence is amended in the open, with every earlier version kept, and withdrawn with a reason rather than deleted. Jobs, clients, filed checklists, work orders, insurance policies, photos and manuals are withdrawn with a reason and kept whole in Withdrawn records. A person on any record is deactivated, never deleted, so their name stays on every flight they flew and every record they signed.
Related#
- Audit Binder
- Occurrences
- Pilot Logbook
- Retention (the position and log windows)