Export specifications.
Documented, versioned, open. Built so a future tool can read your data even if we're gone.
Every export EngineHoursLog produces conforms to one of the formats below. The formats are versioned and forward-compatible — an export file made today will remain readable by future versions of this specification. This is part of our binding commitment in Terms §7.1.
1. JSON — enginehourslog.export v1.0
A single file containing every engine, every hours reading, and every part/service record in your account. Machine-readable, human-readable, diff-friendly. This is the canonical format; CSV and PDF are derived from it.
Top-level shape
{
"format": "enginehourslog.export",
"formatVersion": "1.0",
"exportedAt": "2026-04-21T14:00:00.000Z",
"specUrl": "https://enginehourslog.com/export-specs",
"engines": [ Engine, ... ],
"logs": [ LogEntry, ... ],
"parts": [ PartRecord, ... ]
}
Engine
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Stable identifier. Never reused, never changes. |
name | string | Human-readable name ("S/V Driftwood"). |
identifier | string | Optional serial, hull ID, VIN, or similar. |
category | string enum | One of: marine_diesel, outboard, rv, tractor, generator, heavy, mower, other. New categories may be added; consumers should treat unknown values as opaque. |
hours | integer | Most recent known hours reading. Computed from the latest LogEntry. |
createdAt | string (ISO 8601) | When the engine was added to the log. |
updatedAt | string (ISO 8601) | Last mutation timestamp. |
LogEntry
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Stable identifier. |
engineId | string (UUID) | FK to Engine.id. |
hours | integer | Hours reading at the moment of this entry. |
at | string (ISO 8601) | When the reading was recorded. |
note | string | Optional free-text note. May contain newlines. |
PartRecord
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Stable identifier. |
engineId | string (UUID) | FK to Engine.id. |
component | string | What was replaced/serviced ("Impeller," "Oil & filter"). |
hours | integer | Hours at the time of service. |
at | string (ISO 8601) | When the service occurred. |
vendor | string | Supplier or shop. |
partNum | string | Manufacturer part number. |
cost | number | null | Cost in the account's default currency. |
notes | string | Optional free-text notes. May be paragraphs. May contain newlines. |
Compatibility rules
- Minor version bumps (v1.1, v1.2, ...) may add optional fields. Consumers must ignore fields they don't understand. Any field present in v1.0 will retain its meaning.
- Major version bumps (v2.0) may restructure. We will publish a migration tool that converts v1 exports to v2 losslessly before shipping v2.
- No version is ever removed. Today's v1.0 file is readable by every future version of EngineHoursLog and by anyone implementing the spec.
- Unknown enum values (e.g., a future
category) must be treated as opaque strings by consumers, not as errors.
2. CSV — v1.0
Three files, RFC 4180 compliant, UTF-8, Unix line endings. Designed for Excel, Numbers, Google Sheets, and every command-line tool that eats CSV.
enginehourslog-engines-YYYY-MM-DD.csv
Columns (in order): id, name, identifier, category, hours, createdAt, updatedAt.
enginehourslog-logs-YYYY-MM-DD.csv
Columns: id, engineId, hours, at, note.
enginehourslog-parts-YYYY-MM-DD.csv
Columns: id, engineId, component, hours, at, vendor, partNum, cost, notes.
Quoting and escaping
- All values are escaped per RFC 4180 — fields containing a comma, newline, or double quote are wrapped in double quotes, and interior double quotes are doubled.
nullis represented as an empty field.- Newlines within a
noteornotesfield are preserved inside quoted fields. Most spreadsheet applications handle this; simple line-based tools may not.
3. Sale Packet — PDF v1.0
A human-readable report intended for a prospective buyer, a mechanic, a surveyor, or an estate handoff. Letter size (8.5" × 11"). Four pages:
- Cover. Asset name and identifier; category; current hours; year entered into the log; prepared-for slot; prepared-by slot.
- Summary. Total hours, years of log history, average annual use, number of entries, number of parts/service events, total documented spend. Data-provenance attestation.
- Parts & service by component. Grouped by component, most recent first; each group with a rollup line (event count, average interval between events, total cost) and a detailed table (hours, date, vendor/part, cost, and a full-width note row for paragraph-length notes).
- Hours log in full. Chronological table of every hours reading. Export-format attestation referencing this page and the shutdown guarantee.
The PDF is generated entirely client-side using the browser's native "Save as PDF" print path. No server, no library, no dependencies — the HTML that produces it is simple enough that any future browser can render it unchanged.
Locale
When present on the asset, a locale hint causes the Sale Packet to render in that locale — dates, numbers, currency, and UI chrome. This is because the buyer at the marina speaks the marina's language, not the seller's.
4. If we disappear
Per Terms §7.2, if EngineHoursLog ever ceases operations:
- The export tool remains online for at least twelve months after the shutdown announcement.
- This specification page remains public, hosted on an endowment-funded static mirror. (By the time we're close to shutting down, we will publish the archival-mirror URL here.)
- Any user who previously exported their data can continue using it with any tool that implements this specification.
5. Implementing against this spec
If you want to build something that reads or writes EngineHoursLog exports — a backup tool, a migration script, a second implementation of the app — you don't need our permission. Use the formats on this page. Open an issue on our public issue tracker (link coming) if you find ambiguities. We will update the spec to clarify.
If you want to use this spec as the basis for your own logbook and call it EngineHoursLog, please don't — the brand is ours. The data formats, however, are open for anyone to implement.