LightMySky

The LightMySky pack format

A pack is a portable collection of teaching topics, each with an optional lesson, an optional video, a question pool, and a place in a dependency graph. One pack is one JSON file. The file is self-contained: importing it into any LightMySky account recreates the pack exactly, with fresh ids.

File extension by convention: .lightmysky-pack.json.

Top level

{
  "format": "lightmysky-pack",   // required, exactly this string
  "version": 1,                   // required, exactly 1
  "name": "Chess basics",        // required, 1-60 chars
  "description": "First steps",  // optional, up to 160 chars
  "downloadable": true,           // optional, default false (see below)
  "library": { },                 // optional, ignored on import (see below)
  "topics": [ ]                   // required, 1-200 topic objects
}

downloadable is the author's permission for other people to take the file away. It changes nothing about using the pack inside LightMySky: a public pack can always be switched on for a learner. Leave it out and the file stays yours, offered to nobody as a download.

library is a glossary written by the exporter: it maps every built-in library topic id referenced in after to its human-readable name, e.g. { "mt_WcfaSfVT33": "One-to-one counting" }. Importers ignore it; it exists so a reader knows what the ids mean without looking them up.

Topic

{
  "key": "how-pieces-move",      // recommended; lowercase/digits/dashes, unique in file
  "subject": "Chess",            // required, 1-40 chars; existing subject or a new one
  "name": "How pieces move",     // required, 1-80 chars
  "description": "One line",     // optional, up to 200 chars
  "ages": [6, 10],                // optional [from, to], integers 4-14; default [6, 8]
  "after": ["#basics", "mt_WcfaSfVT33", "One-to-one counting"],
  "lesson": "…",                  // optional, up to 10000 chars, format below
  "videoUrl": "https://…",       // optional http(s) URL; YouTube links play inline
  "questions": [ ]                // optional, up to 50, format below
}

subject may name one of the built-in subjects (Science, Mathematics, English, History, Geography, Civics & Economics, Arts & Music, Design & Technology, Languages, Personal & Social Development, Life Skills, Computing, Learning to Learn) to extend it, or any new name to create a subject of your own.

A topic with no questions is never served in learning sessions; its lesson is still readable. Give every topic at least one question if you want it practiced.

The dependency graph ("after")

after lists what must be learned before this topic unlocks. Each entry is a string in exactly one of three forms, checked in this order:

  1. "#<key>" — another topic **in this file**, by its key. This is the only way to reference custom topics, because their database ids are assigned at import and differ per account. Never reference a custom topic by a ct_… id in a pack file.
  2. "mt_…" — a built-in library topic by its stable id. Ids are stable across accounts and versions; prefer them for library references. Discover ids via GET /api/v1/topics?q=<search> (no auth needed).
  3. Any other string — an **exact, case-insensitive name** of a library topic, e.g. "One-to-one counting". Convenient to write by hand, but it must match exactly one library topic or the import is rejected.

Rules the importer enforces:

  • every #key must exist in the file; a topic cannot come after itself
  • #key edges must not form a cycle
  • unknown ids or names are errors (the message tells you which entry)
  • at most 20 entries per topic

Semantics after import: a learner sees the topic only once every entry in after is known. A library prerequisite below the learner's configured teaching floor counts as known. Prerequisites pointing at deleted topics are waived rather than blocking.

Lesson text

Plain text. Blank lines split it into sections. A section's first word can mark its kind:

  • tip: renders as a "good to know" callout
  • example: (or try:) renders as a try-it-together activity
  • recap: becomes the one-line summary shown with a star; write at most one
  • anything else is a plain teaching paragraph

Example:

Every chess piece has its own way of moving.

tip: The knight is the only piece that can jump over others.

example: Put a rook on an empty board and count its squares.

recap: Each piece moves its own way, and the knight jumps.

Questions

Three types. Every question needs prompt and explanation (shown after answering). difficulty is 1 (easy), 2 (medium, default), or 3 (hard); the session picks difficulty to match the learner's current strength.

{ "type": "mcq", "prompt": "Which piece can jump?",
  "choices": ["Knight", "Rook"], "answerIndex": 0,
  "explanation": "Only the knight jumps.", "difficulty": 1 }

{ "type": "true_false", "prompt": "Bishops move on diagonals.",
  "answer": true, "explanation": "They never leave their color." }

{ "type": "numeric", "prompt": "How many squares on a chessboard?",
  "answer": 64, "explanation": "8 rows of 8." }

choices holds 2-4 strings and answerIndex is 0-based.

Import semantics

  • Importing always creates a **new private pack** owned by the importer;

nothing merges with or overwrites existing packs.

  • All topics get fresh ct_… ids; #key references are rewired to them.
  • Sharing, per-learner enablement, and learner progress never travel in the

file; only content does.

  • Validation is all-or-nothing: any error rejects the whole file, and every

error is reported with its JSON path, e.g. topics[2].after[0]: no topic in this file has key "basics".

Endpoints

  • GET /api/v1/packs/:id/export (API key) — download a pack as this format
  • POST /api/v1/packs/import (API key) — body is the file; returns the new pack
  • GET /api/v1/packs/format (no auth) — this document
  • GET /learn/packs/:id/file (no auth) — download a pack from the public gallery
  • On the website: Download on a pack's page, Upload on the Packs page.

Complete example

{
  "format": "lightmysky-pack",
  "version": 1,
  "name": "Chess basics",
  "description": "Board, pieces, and first mates",
  "library": { "mt_WcfaSfVT33": "One-to-one counting" },
  "topics": [
    {
      "key": "how-pieces-move",
      "subject": "Chess",
      "name": "How pieces move",
      "ages": [6, 10],
      "after": ["mt_WcfaSfVT33"],
      "lesson": "Every piece has its own way of moving.\n\ntip: The knight jumps.\n\nrecap: Each piece moves its own way.",
      "videoUrl": "https://www.youtube.com/watch?v=fKxG8KjH1Qg",
      "questions": [
        { "type": "mcq", "prompt": "Which piece can jump over other pieces?",
          "choices": ["Knight", "Rook"], "answerIndex": 0,
          "explanation": "Only the knight jumps.", "difficulty": 1 }
      ]
    },
    {
      "key": "simple-checkmates",
      "subject": "Chess",
      "name": "Simple checkmates",
      "ages": [7, 11],
      "after": ["#how-pieces-move"],
      "questions": [
        { "type": "true_false", "prompt": "A king can be captured.",
          "answer": false, "explanation": "The game ends at checkmate, before capture." }
      ]
    }
  ]
}
The pack file format · LightMySky