Skip to content

Developing a .tuesh extension

.tuesh is the owner-published extension format. A package is a bounded ZIP archive of prebuilt browser assets and is never extracted into the application filesystem. Sandbox packages execute in an opaque-origin iframe; explicitly native packages execute as trusted code in the hydrated Study Hub page. The owner enables the Plugin Hub before an upload can be accepted.

Native author workflow:

npm install
npm run build:native-plugin -- examples/native-plugins/native-ui-test
npm test -- src/plugins/native

The source manifest uses runtime: "native" and numeric nativeApiMajor: 1; the build command writes the uploaded nativeApiVersion: "1" spelling and supplies host React at runtime. Native code is trusted page code, not an isolation boundary.

Archive layout

manifest.json
main.js
worker.js          # optional background entry
styles.css         # optional guest-only stylesheet
assets/icon.svg    # optional assets

manifest.json must name a lowercase dashed id, a package version, pluginApiVersion major, courseOsCompatibility, at least one entry.ui or entry.worker, a requestedCapabilities array, and a contributes object. Every entry path must point to a file in the archive. The host derives content type from the safe extension, not from author metadata.

The current limits are enforced before package bytes are accepted:

  • archive: 1 MiB maximum;
  • files: 64 maximum;
  • one file: 2 MiB maximum;
  • total uncompressed bytes: 4 MiB maximum;
  • accepted extensions: json, js, mjs, css, svg, png, jpg, jpeg, webp, woff2, txt, and md.

Path traversal, absolute paths, dot-segments, case-insensitive collisions, unsupported extensions, compression bombs, malformed ZIP data, and a missing manifest are rejected. The registry is not changed when validation or read-back verification fails.

Capabilities and compatibility

Plugin API major 1 currently exposes ui.page, ui.panel, ui.block, commands.register, course.read, course.write, storage.namespaced, events.subscribe, search.provide, review.provide, transfer.provide, editor.actions, worker.background, and network.fetch. ai.complete is reserved. Required unsupported capabilities reject the package; optional unsupported capabilities are omitted from the grant. Course-scoped capabilities require a course surface.

The host grants exactly the compatible capabilities requested by the manifest. It computes a SHA-256 for every file and a package integrity hash over the sorted path sha256 list. The immutable revision identity is:

<plugin-id>@<package-version>+<integrity-hash>

Versioning rule

Always bump the package version when updating an extension. Every modification to an extension's implementation or metadata must be accompanied by an incremented version in manifest.json (using semantic versioning: patch for fixes, minor for features/UI updates, major for breaking changes). The host uses the package version to key the immutable revision identity and prevent client cache collision. After updating files, re-pack with npm run pack:plugin <directory> or rebuild with npm run build:native-plugin.

The host stores package files, the copied manifest, and the installation record under the separate plugins/ storage alphabet. It reads every file back before conditionally updating the one mutable registry document.

Permission review

The first upload and every update that adds a capability remain unpublished until the owner reviews the delta. Every native revision also requires explicit native-runtime approval, even when its capabilities are unchanged. Dropped capabilities do not need approval. Approval activates the exact verified revision and records the expanded grant; publishing then makes it visible in the reader catalog. Every reader starts with an optional plugin disabled and may enable it everywhere or only for selected courses on that browser. The owner can unpublish, roll back, uninstall, or explicitly delete namespaced data; uninstall alone never deletes the data.

The Hub resolves master kill switch, quarantine, required-course dependencies, catalog availability, and personal everywhere/selected/off preferences deterministically. Legacy or absent inherit preferences resolve to off. Course publication refuses a required plugin revision that is not installed and verified.

Verification

Run npm run check and npm run build. Generated fixtures are reproduced by src/course-os/plugins/fixtures.generate.test.ts. With the Hub enabled, a compatible active revision is revalidated on the server and then becomes available to the existing isolated sandbox surfaces or the trusted native runtime without a deployment. Exact revisions pinned by a course remain selectable while retained in the installed registry.