Authoring modules
The data model: how a module document is shaped, how verses are referenced, and how annotations attach to text. For the mechanics of producing the file, see Building a .lamp file.
The pipeline
A module is authored as JSON against a published schema, then converted to SQLite and compressed. This page covers the first step — the shape of the JSON. The conversion is on Building a .lamp file.
source text → module JSON → SQLite → raw DEFLATE → module.lamp
└── this page ──┘ └──────── building a .lamp ───────┘The container, briefly
A .lamp file is a SQLite database compressed with raw DEFLATE (zlib level 9, wbits=-15). One file holds one module — one translation, one book of a commentary, one highlight set — and is named after it. Compression is typically 95% or better.
The library bundled inside the app keeps many modules in a single database, so its tables carry identifier columns to separate co-resident modules. A standalone .lamp names its one module in a module_format table instead; where its tables keep a module_id column as well, every row must carry that same ID.
Verse references
Every reference in every schema uses a single integer format, BBCCCVVV: two digits of book number, three of chapter, three of verse.
| Reference | Value |
|---|---|
| Genesis 1:1 | 1001001 |
| Matthew 1:1 | 40001001 |
| Ephesians 2:8 | 49002008 |
Ranges are expressed as a start and end pair, conventionally sv and ev. Because the encoding sorts correctly as an integer, range queries and ordering come free.
Annotated text
Text fields are either a plain string or an object carrying the string plus offset-based annotations. Keeping annotations out of the text — rather than wrapping it in markup — means the plain text stays clean for search, display and speech.
{
"text": "In the beginning God created the heaven and the earth.",
"annotations": [
{
"type": "strongs",
"start": 7,
"end": 16,
"text": "beginning",
"data": { "strongs": "H7225" }
},
{
"type": "strongs",
"start": 17,
"end": 20,
"text": "God",
"data": { "strongs": "H430", "lemma": "אֱלֹהִים" }
}
]
}start is inclusive and end exclusive, so text[start:end] must equal the annotation’s own text field. That redundancy is deliberate — it is what lets a validator catch a bad offset.
Annotation types vary a little by schema; the common ones are:
| Type | Carries |
|---|---|
strongs | Strong’s number, optional lemma and morphology code |
scripture | sv, optional ev, or a list of refs |
red-letter | Words of Christ |
greek / hebrew | Original-language text, optionally with a Strong’s number |
lexicon-ref | Cross-reference to another lexicon entry |
link | External URL |
emphasis | Bold, italic or underline |
The schemas
Each module type has a JSON Schema, all sharing common definitions for annotations, references, metadata and media. Every one is downloadable here, along with a worked sample module that validates against it.
- book_schema.json11.0 KB
- commentary_example.json3.5 KB
- commentary_schema.json16.6 KB
- commentary_series_schema.json5.1 KB
- commentary_simple_example.json995 B
- devotional_schema.json12.1 KB
- highlight_schema.json3.7 KB
- lexicon_mapping_schema.json1.5 KB
- lexicon_schema.json17.3 KB
- notes_schema.json7.6 KB
- plan_schema.json2.8 KB
- quiz_schema.json6.4 KB
- translation_schema.json10.3 KB
| Schema | Shape |
|---|---|
translation_schema.json | Metadata, then books → chapters → verses. Chapters carry section headings; verses carry content, footnotes, and paragraph and poetry markers. |
lexicon_schema.json | Entries keyed by identifier, with lemma, transliteration, pronunciation, definition, numbered senses, usage and references. |
lexicon_mapping_schema.json | A map from one keying system to another — Strong’s to BDB, for example, where one key may map to several entries. |
commentary_schema.json | One file per book: chapters → sections → pericopae → verse commentary, with footnotes and an abbreviations list. Version 2.2 adds an optional source-ordered form, below. |
commentary_series_schema.json | Series-level metadata: editor, publisher, preface, abbreviations, bibliography and the volume list. |
book_schema.json | A long-form publication: metadata, then sections — front matter, parts, chapters, appendices — nesting to any depth, each holding content blocks. Footnotes and media are defined once for the whole book. |
notes_schema.json | A simplified commentary: one file per book, chapters with an introduction and verse notes keyed by sv/ev. |
devotional_schema.json | Metadata, summary, and content either as a flat block array or as introduction/sections/conclusion, plus footnotes. |
highlight_schema.json | Verses, each with highlight spans carrying start and end character offsets, a style and a colour. |
plan_schema.json | Reading assignments by day. |
quiz_schema.json | Questions and answers per reading, graded by age group. |
Commentary schema 2.2
Version 2.2 adds a second way to write a pericope, meant for commentaries converted from a printed edition. Instead of verses, a pericope may carry content: an array of classified blocks — heading, translation, commentary, paragraph and so on — in the order they appear in the source, each recording where in the source it came from. Do not reorder them by role or by verse; the app shows them in the order given.
Footnotes can likewise be defined once for the whole book, in a top-level footnotes array. A pericope lists the ones it uses in footnoteIds, and a block’s text marks where each one falls with footnote_refs.
Both are optional. The earlier structure — chapters → sections → pericopae → verses, with footnotes on the pericope or the verse — is still accepted, and it is the one the sample commentary uses.
Worked example: a translation
{
"meta": {
"schemaVersion": "1.0",
"id": "MYBIBLEs",
"type": "translation",
"name": "My Bible",
"abbreviation": "MYBIBLE",
"language": "en",
"textDirection": "ltr",
"translationPhilosophy": "formal",
"versification": "standard",
"features": { "strongs": true, "morphology": true }
},
"books": [
{
"id": "Gen",
"name": "Genesis",
"number": 1,
"testament": "OT",
"chapters": [
{
"chapter": 1,
"headings": [{ "text": "The Creation", "beforeVerse": 1, "level": 1 }],
"verses": [
{
"v": 1,
"ref": 1001001,
"content": { "text": "At first, God made the sky and the land.", "annotations": [] },
"paragraph": true
}
]
}
]
}
]
}Translation identifiers
By convention a trailing s marks a Strong’s-tagged edition. The identifier must be unique across installed modules, since it is what highlights and settings refer to.
Worked example: highlights
This one marks the words God made in the example above, beginning at character 10 and ending at 18.
{
"meta": {
"schemaVersion": "1.0",
"id": "my_highlights",
"type": "highlights",
"name": "My Highlights",
"translationId": "MYBIBLEs"
},
"verses": [
{
"ref": 1001001,
"highlights": [
{ "sc": 10, "ec": 18, "style": 0, "color": "FFE066" }
]
}
]
}sc and ec are character offsets within the verse text of the named translation, start inclusive and end exclusive. style is 0 for a filled highlight, 1 for a solid underline, 2 for dashed and 3 for dotted. color is a hex code, written without the #.
Media
Notes, devotionals, commentaries and lexicons can reference images and audio. In markdown interchange these live in a media/ folder beside the document, with UUID-prefixed filenames to avoid collisions; in a shared package they travel inside the ZIP. Supported formats are JPEG, PNG, GIF, WebP and HEIC for images, and M4A, MP3, WAV, AAC and OGG for audio — M4A being the recommended choice.
Building and installing
Once the JSON is right, turning it into an installable file is four steps: create the tables, insert your rows, compress with raw DEFLATE, verify. That is covered in full — with the table definitions for every type and a complete reference implementation — on Building a .lamp file.
A working sample module of each type, with its source JSON, is on Downloads. Starting from one of those is usually faster than starting from the schema.