Into the VLE without an integration project
Zayeed's server is an LTI 1.3 tool. A teacher places the activity, a student clicks it, and the mark goes back to the gradebook. There is no bespoke connector to commission and no nightly CSV to reconcile.
LTI 1.3
- Deep Linking — the teacher picks a quiz from their own library while placing the activity, rather than pasting an opaque ID.
- Launch — the student's click lands them in that quiz, already signed in.
- Assignment and Grade Services — the mark is written back to the gradebook.
- Offline-safe grading — marking still happens on the student's device, so a grade earned with no network is queued and delivered when the network returns.
# point the server at a signing key and the platforms it trusts export ZAYEED_TOOL_URL=https://exams.example.edu # where the platform reaches the tool export ZAYEED_APP_URL=https://app.example.edu # where a launched student lands export ZAYEED_LTI_KEY=./lti-private.pem # generated on first use if absent export ZAYEED_LTI_CONFIG=./lti-platforms.json # issuer, client id, auth + token URLs
/lti/config describes the tool for a platform administrator and /lti/jwks publishes its public key. The private key never leaves the server, and the gradebook credential never reaches a device.
QTI 3.0
Import and export both run through a dedicated package. Where a construct does not map cleanly in either direction, the item carries a note saying so. Silent loss during an interchange is worse than a refusal, because nobody finds it until the paper has already been sat.
Packs
The .mcq container is the interchange format when there is no network at all: items, media, rules, question sets and the on-screen keyboard layout in one file, encrypted and signed, with a lossless bridge to and from the database. It is specified rather than merely shipped — the specification and its test vectors are published, so a reader can be written from them alone — and every shell still opens the .mcqx ZIP it replaced and the legacy MCQPro banks that came before that, deciding which by reading the bytes.
The same container carries a sync bundle: the changes one device has that another has not seen, rather than a snapshot. Hand it over on a memory stick and it merges into whatever the other machine has done since; apply it twice and the second time changes nothing.
Self-hosting
The server runs on Node against SQLite by default, or Postgres by setting DATABASE_URL. Both are generated from one schema definition and pinned by a parity test, so self-hosting is the same product rather than a reduced edition. For air-gapped estates, the sync server and the shells never require an outbound connection to us.
Still deciding?
The fastest way to judge a marker is to try to break it. Run a paper, answer it badly on purpose, and read the reason next to every mark.