Scope
v1 is intentionally narrow.
The package spec supports learner-facing course activities, games,
quizzes, simulations, tutors, and practice tools. It does not support
arbitrary backend code, direct LMS API access, direct D1 database
access, arbitrary outbound HTTP, or direct grade writes.
The goal
- Make app generation easy enough for real course use.
- Make review concrete enough for institutional governance.
- Make unsafe shortcuts invalid by default.
Layout
The package is small enough to inspect.
app/
manifest.json
dist/
index.html
assets/*
content/
activity.json
scoring/
rubric.json
preview/
fixtures.json
tests.json
grading/
specs/
*.js
evidence/
example-output.json
manifest.json and dist/index.html are required.
The rest depends on the app. Once a package version is signed, treat it
as immutable.
Manifest
The manifest is the review contract.
Required fields
schema_version app_id version title owner entrypoint capabilities roles grading
Optional fields
description icon install_scope browser content_files preview authoring
{
"schema_version": "1",
"app_id": "chapter-4-asteroids",
"version": "0.1.0",
"title": "Chapter 4 Asteroids",
"owner": { "type": "user", "id": "instructor_123" },
"entrypoint": "/dist/index.html",
"roles": ["learner", "instructor"],
"install_scope": "course",
"capabilities": [
"read_launch_context",
"read_activity_content",
"submit_attempt_event",
"submit_evidence_artifact",
"finalize_attempt",
"read_local_state",
"write_local_state"
],
"grading": { "mode": "browser", "max_score": 100 },
"content_files": ["/content/activity.json"],
"preview": {
"fixtures_file": "/preview/fixtures.json",
"tests_file": "/preview/tests.json"
},
"authoring": {
"kind": "browser_autograder",
"grader_spec_files": ["/grading/specs/checks.spec.js"],
"evidence_example_file": "/evidence/example-output.json"
}
}
Fields
What the important fields mean.
schema_versionString. v1 starts at "1".
app_idThe stable logical id for the app across versions.
versionThe immutable artifact version. Semver works well.
ownerThe responsible owner, shaped as { "type": "user", "id": "user_123" } in v1.
entrypointThe HTML entry file inside the package, usually /dist/index.html.
rolesAllowed values are learner and instructor.
install_scopeAllowed values are course and assignment. Default is course.
browserA review request, not a permission grant. v1 supports fullscreen and clipboard_write, both false by default.
Allowed v1 capabilities
read_launch_context read_activity_content submit_attempt_event submit_evidence_artifact finalize_attempt read_local_state write_local_state
submit_evidence_artifact requires finalize_attempt. It keeps structured JSON evidence and optional screenshot evidence on the same Lantern-owned path.
Grading modes
declarative: Lantern computes a score from reviewed rules. manual: hold for instructor review. completion: credit follows completion. browser: Lantern runs reviewed browser grader specs.
Authoring fields
authoring describes reviewable artifacts. It does not grant runtime permissions.
kind = "browser_autograder" grader_spec_files under grading/specs/*.js evidence_example_file, usually /evidence/example-output.json
SDK
The SDK is small by design.
type LaunchContext = {
userRole: 'learner' | 'instructor';
courseId: string;
assignmentId?: string;
activityId: string;
submissionMode: 'standard' | 'anonymous_submission';
};
declare function getLaunchContext(): Promise<LaunchContext>;
declare function getActivityContent<T = unknown>(): Promise<T>;
declare function readLocalState<T = unknown>(): Promise<T | null>;
type AttemptEvent =
| {
type: 'answer';
questionId: string;
answer: string | string[];
correct?: boolean;
scoreGiven?: number;
scoreMaximum?: number;
timestamp: string;
}
| { type: 'progress'; checkpoint: string; value: number; timestamp: string }
| { type: 'complete'; timestamp: string };
declare function emitAttemptEvent(event: AttemptEvent): Promise<void>;
declare function finalizeAttempt(input: {
completionState: 'completed' | 'abandoned';
}): Promise<{ accepted: true }>;
declare function writeLocalState<T = unknown>(value: T): Promise<void>;
Forbidden SDK surface
getCanvasAccessToken() writeGrade() runSql() fetch(url) assumeRole() readFullRoster()
If a package needs one of these, it is outside the v1 trust boundary.
Review
Publication depends on evidence, not hope.
Preview contract
- Fake launch context.
- Fake course and assignment ids.
- Fake learner and instructor roles.
- Fake attempt ids.
- Visible capability log.
- Fake scoring response.
Review contract
- Manifest validation passed.
- Bundle is present.
- Preview is runnable.
- Accessibility and security checks passed or are flagged.
- Artifact is signed.
- Reviewer is recorded.
Courses install an app version, not just an app id. That gives the
institution reproducibility, review history, rollback, and auditable
deployment.
If someone needs raw LMS tokens, a small backend, or one arbitrary URL,
the v1 answer is no. That discipline is the point of the package spec.