Skip to content

LTI 1.3 — conformance status

Generated by bun run conformance:render — do not edit by hand.

Denominator: pinned Coverage Map docs/conformance/maps/lti-v1.3.json (generated 2026-06-25). See conform-ed ADR-0013 (map) and emergent ADR-0028 (overlay).

Profile Requirements Done Partial Other Progress
ags 8 8 0 0 ████████████████ 100%
core 5 5 0 0 ████████████████ 100%
deep-linking 4 4 0 0 ████████████████ 100%
nrps 4 4 0 0 ████████████████ 100%
platform-ags 8 8 0 0 ████████████████ 100%
platform-core 5 5 0 0 ████████████████ 100%
platform-deep-linking 4 4 0 0 ████████████████ 100%
platform-dynamic-registration 2 2 0 0 ████████████████ 100%
platform-nrps 4 4 0 0 ████████████████ 100%
platform-proctoring 2 2 0 0 ████████████████ 100%
platform-security 4 3 0 1 ████████████░░░░ 75%
proctoring 2 2 0 0 ████████████████ 100%
security 4 3 0 1 ████████████░░░░ 75%
Req Level Status Evidence Statement
LTI-AGS-1 MUST ✅ done lti:1.3:conf:ags/LTI-AGS-1 A tool reads the AGS endpoint claim — the lineitems URL and the scopes it was granted (lineitem, lineitem.readonly, result.readonly, score) — and restricts itself to those scopes. (The claim is an LTI launch claim, not part of the AGS OpenAPI, so it has no schema anchor here.)
LTI-AGS-2 MUST ✅ done lti:1.3:conf:ags/LTI-AGS-2 A line item MUST carry a label (the gradebook-column title) and a scoreMaximum.
LTI-AGS-3 MUST ✅ done lti:1.3:conf:ags/LTI-AGS-3 A line item is identified by an id URL present on all responses, and MAY bind to a resource link (resourceLinkId) and a tool resource (resourceId).
LTI-AGS-4 MUST ✅ done lti:1.3:conf:ags/LTI-AGS-4 The line item container (GET /lineitems) returns a paged array of line items, filterable by resource_link_id, resource_id and tag and paged by limit/page.
LTI-AGS-5 MUST ✅ done lti:1.3:conf:ags/LTI-AGS-5 A tool publishes a score by POSTing to the scores endpoint; the Score MUST carry userId, activityProgress, gradingProgress and timestamp.
LTI-AGS-6 MUST ✅ done lti:1.3:conf:ags/LTI-AGS-6 When scoreGiven is present it MUST be accompanied by scoreMaximum (the scale it is given in).
LTI-AGS-7 MUST ✅ done lti:1.3:conf:ags/LTI-AGS-7 The result container (GET …/results, filterable by user_id) returns results; a Result references its line item (scoreOf), the user (userId) and the achieved and maximum scores (resultScore / resultMaximum).
LTI-AGS-8 MUST ✅ done lti:1.3:conf:ags/LTI-AGS-8 A tool manages line items over the full REST surface: create (POST /lineitems) and read/update/delete the single line item (GET/PUT/DELETE …/lineitems/{lineItemId}).
Req Level Status Evidence Statement
LTI-CORE-1 MUST ✅ done lti:1.3:conf:core/LTI-CORE-1 A tool MUST implement the OpenID Connect third-party-initiated login: on the login initiation it returns an authentication request to the platform’s authorization endpoint. Every launch follows OIDC — there are no exceptions.
LTI-CORE-2 MUST ✅ done lti:1.3:conf:core/LTI-CORE-2 A tool MUST validate the id_token: a JWT signed (RS256) with the platform key resolved via its JWKS, with valid iss, aud, exp and iat, and the nonce the tool issued — rejecting any that fail.
LTI-CORE-3 MUST ✅ done lti:1.3:conf:core/LTI-CORE-3 A resource-link launch MUST carry the required claims: message_type=LtiResourceLinkRequest, version=1.3.0, the deployment_id, the target_link_uri, and the resource_link claim with an id.
LTI-CORE-4 MUST ✅ done lti:1.3:conf:core/LTI-CORE-4 The launch MUST carry the roles claim (an array of role URIs) binding the user to the context; the tool authorizes from it.
LTI-CORE-5 MUST ✅ done lti:1.3:conf:core/LTI-CORE-5 A tool MUST reject a launch missing a required claim, carrying the wrong LTI version, with an invalid iat/exp, or whose JWT signature or kid does not verify.
Req Level Status Evidence Statement
LTI-DL-1 MUST ✅ done lti:1.3:conf:deep-linking/LTI-DL-1 A LtiDeepLinkingRequest carries the deep_linking_settings claim with deep_link_return_url, accept_types and accept_presentation_document_targets.
LTI-DL-2 MUST ✅ done lti:1.3:conf:deep-linking/LTI-DL-2 The request lti_message_type MUST be LtiDeepLinkingRequest and the tool’s reply LtiDeepLinkingResponse.
LTI-DL-3 MUST ✅ done lti:1.3:conf:deep-linking/LTI-DL-3 The tool returns the selection as a signed JWT carrying the content_items claim (a JSON array of the selected content items: ltiResourceLink, link, file, html or image).
LTI-DL-4 MUST ✅ done lti:1.3:conf:deep-linking/LTI-DL-4 After encoding the response JWT the tool MUST redirect the workflow to the deep_link_return_url via an auto-submitted form.
Req Level Status Evidence Statement
LTI-NRPS-1 MUST ✅ done lti:1.3:conf:nrps/LTI-NRPS-1 A tool consumes the NRPS claim (context_memberships_url + service_versions) from the launch to locate the membership service.
LTI-NRPS-2 MUST ✅ done lti:1.3:conf:nrps/LTI-NRPS-2 A tool retrieves the membership container (media type application/vnd.ims.lti-nrps.v2.membershipcontainer+json) via GET on the context membership URL, following Link-header paging.
LTI-NRPS-3 MUST ✅ done lti:1.3:conf:nrps/LTI-NRPS-3 Each member carries a user_id (the launch sub) and a roles array; a member with status Inactive/Deleted MUST be honoured (not treated as active).
LTI-NRPS-4 MUST ✅ done lti:1.3:conf:nrps/LTI-NRPS-4 The membership request MUST be authorized by an OAuth 2.0 access token bearing the scope https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly.
Req Level Status Evidence Statement
PLAT-AGS-1 MUST ✅ done lti:1.3:conf:platform-ags/PLAT-AGS-1 A platform MUST include the AGS endpoint claim (the lineitems URL and the granted scopes) in launches, and MUST enforce that a tool’s service calls stay within the scopes it was granted.
PLAT-AGS-2 MUST ✅ done lti:1.3:conf:platform-ags/PLAT-AGS-2 A platform MUST accept line item creation (POST /lineitems), requiring a label (the gradebook-column title) and a scoreMaximum.
PLAT-AGS-3 MUST ✅ done lti:1.3:conf:platform-ags/PLAT-AGS-3 A platform MUST assign each line item an id URL returned on every response, and MUST honour a binding to a resource link (resourceLinkId) and a tool resource (resourceId).
PLAT-AGS-4 MUST ✅ done lti:1.3:conf:platform-ags/PLAT-AGS-4 A platform MUST serve the line item container (GET /lineitems) as a paged array, filterable by resource_link_id, resource_id and tag and paged by limit/page.
PLAT-AGS-5 MUST ✅ done lti:1.3:conf:platform-ags/PLAT-AGS-5 A platform MUST accept a score POSTed to the scores endpoint, requiring userId, activityProgress, gradingProgress and timestamp.
PLAT-AGS-6 MUST ✅ done lti:1.3:conf:platform-ags/PLAT-AGS-6 When an accepted score carries scoreGiven the platform MUST require scoreMaximum (the scale it is given in) alongside it.
PLAT-AGS-7 MUST ✅ done lti:1.3:conf:platform-ags/PLAT-AGS-7 A platform MUST serve the result container (GET …/results, filterable by user_id); each Result references its line item (scoreOf), the user (userId) and the achieved and maximum scores (resultScore / resultMaximum).
PLAT-AGS-8 MUST ✅ done lti:1.3:conf:platform-ags/PLAT-AGS-8 A platform MUST serve the full single-line-item REST surface: read, update and delete the line item (GET/PUT/DELETE …/lineitems/{lineItemId}).
Req Level Status Evidence Statement
PLAT-CORE-1 MUST ✅ done lti:1.3:conf:platform-core/PLAT-CORE-1 A platform MUST implement the OpenID Connect third-party-initiated login flow: it exposes a login-initiation endpoint and, on the tool’s authentication request, returns the signed id_token launch to the tool’s registered redirect_uri.
PLAT-CORE-2 MUST ✅ done lti:1.3:conf:platform-core/PLAT-CORE-2 A platform MUST mint the id_token as a JWT signed RS256 with the platform key (resolvable via the platform’s JWKS), carrying iss (the platform issuer), aud (the tool’s client_id), sub (the user), exp and iat, and the nonce from the authentication request.
PLAT-CORE-3 MUST ✅ done lti:1.3:conf:platform-core/PLAT-CORE-3 A resource-link launch the platform issues MUST carry the required claims: message_type=LtiResourceLinkRequest, version=1.3.0, the deployment_id, the target_link_uri, and the resource_link claim with an id.
PLAT-CORE-4 MUST ✅ done lti:1.3:conf:platform-core/PLAT-CORE-4 The launch the platform issues MUST carry the roles claim (an array of role URIs from the LTI vocabulary) binding the user to the context.
PLAT-CORE-5 MUST ✅ done lti:1.3:conf:platform-core/PLAT-CORE-5 A platform MUST issue only well-formed launches — never one missing a required claim, carrying the wrong LTI version, or with an invalid iat/exp — and MUST sign every launch with its private key.
Req Level Status Evidence Statement
PLAT-DL-1 MUST ✅ done lti:1.3:conf:platform-deep-linking/PLAT-DL-1 A platform MUST issue a LtiDeepLinkingRequest carrying the deep_linking_settings claim with deep_link_return_url, accept_types and accept_presentation_document_targets.
PLAT-DL-2 MUST ✅ done lti:1.3:conf:platform-deep-linking/PLAT-DL-2 The platform’s request lti_message_type MUST be LtiDeepLinkingRequest and it MUST accept the tool’s reply as LtiDeepLinkingResponse.
PLAT-DL-3 MUST ✅ done lti:1.3:conf:platform-deep-linking/PLAT-DL-3 A platform MUST accept the tool’s signed response JWT, validating the content_items claim and each selected content item (ltiResourceLink, link, file, html or image) before placing it.
PLAT-DL-4 MUST ✅ done lti:1.3:conf:platform-deep-linking/PLAT-DL-4 A platform MUST receive the tool’s deep-linking response at the deep_link_return_url it advertised and persist the selected content items as placements in the originating context.
Req Level Status Evidence Statement
PLAT-DR-1 MUST ✅ done lti:1.3:conf:platform-dynamic-registration/PLAT-DR-1 A platform supporting Dynamic Registration MUST expose an OpenID Connect openid-configuration document advertising its issuer, the authorization/token/JWKS endpoints, the registration endpoint and the LTI platform capabilities it offers.
PLAT-DR-2 MUST ✅ done lti:1.3:conf:platform-dynamic-registration/PLAT-DR-2 A platform MUST accept a tool’s registration request at the registration endpoint (bearer registration access token) and return the registered client configuration — the assigned client_id and the negotiated LTI tool configuration.
Req Level Status Evidence Statement
PLAT-NRPS-1 MUST ✅ done lti:1.3:conf:platform-nrps/PLAT-NRPS-1 A platform MUST include the NRPS claim (context_memberships_url + service_versions) in launches for contexts where the membership service is available.
PLAT-NRPS-2 MUST ✅ done lti:1.3:conf:platform-nrps/PLAT-NRPS-2 A platform MUST serve the membership container (media type application/vnd.ims.lti-nrps.v2.membershipcontainer+json) on GET of the context membership URL, paging with the Link header when the roster spans pages.
PLAT-NRPS-3 MUST ✅ done lti:1.3:conf:platform-nrps/PLAT-NRPS-3 Each member the platform serves MUST carry a user_id (the same value as the launch sub) and a roles array, and an inactive/withdrawn member MUST be marked with the appropriate status rather than omitted silently.
PLAT-NRPS-4 MUST ✅ done lti:1.3:conf:platform-nrps/PLAT-NRPS-4 A platform MUST require the membership request to bear an OAuth 2.0 access token with the scope https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly, rejecting an unscoped or unauthorized read.
Req Level Status Evidence Statement
PLAT-PROC-1 MUST ✅ done lti:1.3:conf:platform-proctoring/PLAT-PROC-1 A platform MUST send the LtiStartProctoring message (with the proctoring settings and the start_assessment_url) to the proctoring tool, and accept the tool’s launch of the assessment via that start_assessment_url.
PLAT-PROC-2 MUST ✅ done lti:1.3:conf:platform-proctoring/PLAT-PROC-2 A platform MUST accept the LtiEndAssessment message returning control at the end of the attempt (and MAY honour an Assessment Control Service verdict during it).
Req Level Status Evidence Statement
PLAT-SEC-1 MUST ✅ done lti:1.3:conf:platform-security/PLAT-SEC-1 All JWT signing MUST use asymmetric RSA-256; the platform signs the id_token only with its private key (symmetric cryptosystems are forbidden).
PLAT-SEC-2 MUST ➖ not-applicable All communication endpoints MUST be secured with TLS (SSL alone is forbidden).
PLAT-SEC-3 MUST ✅ done lti:1.3:conf:platform-security/PLAT-SEC-3 A platform MUST expose its public keys for tools to verify the id_token signature — a JWKS URL resolving the kid of every issued launch.
PLAT-SEC-4 MUST ✅ done lti:1.3:conf:platform-security/PLAT-SEC-4 A platform MUST operate an OAuth 2.0 token endpoint accepting the client-credentials grant with private_key_jwt client authentication: it verifies the tool’s signed client-assertion against the tool’s registered JWKS, checks the requested scopes, and issues a scoped access token.
Req Level Status Evidence Statement
LTI-PROC-1 MUST ✅ done lti:1.3:conf:proctoring/LTI-PROC-1 A proctoring tool receives the LtiStartProctoring message (with the proctoring_settings and the start_assessment_url) and only launches the assessment via the start_assessment_url once proctoring setup completes.
LTI-PROC-2 MUST ✅ done lti:1.3:conf:proctoring/LTI-PROC-2 The tool returns control to the platform with an LtiEndAssessment message at the end of the attempt (and MAY surface an Assessment Control Service verdict during it).
Req Level Status Evidence Statement
LTI-SEC-1 MUST ✅ done lti:1.3:conf:security/LTI-SEC-1 All JWT signing and verification MUST use asymmetric RSA-256; symmetric cryptosystems are forbidden and a JWT MUST be signed only with the holder’s private key.
LTI-SEC-2 MUST ➖ not-applicable All communication endpoints MUST be secured with TLS (SSL alone is forbidden).
LTI-SEC-3 MUST ✅ done lti:1.3:conf:security/LTI-SEC-3 A tool MUST expose its public keys for the platform to verify the tool’s JWTs — a JWKS URL (recommended) or a registered public key.
LTI-SEC-4 MUST ✅ done lti:1.3:conf:security/LTI-SEC-4 To call a service (NRPS / AGS) a tool MUST obtain an OAuth 2.0 access token via the client-credentials grant, presenting a signed client-assertion JWT and requesting the service scope.

Information-model coverage (vs conform-ed model)

Section titled “Information-model coverage (vs conform-ed model)”
  • 270 literal items — 189 modelled ✅, 0 partial 🟡, 0 gaps ❌
  • Silent gaps (in spec, not modelled): none
  • conform-ed extensions (modelled, not in spec): 7
  • Transport surface (REST binding, separate axis): 7 operations, 6 query parameters, 0 security scheme(s) — see the transport requirements above
  • lti:1.3:conf:ags/LTI-AGS-1
    • emergent records an AGS launch-context snapshot from the endpoint claim (the lineitems URL + scopes) when AGS services are present on the launch.
  • lti:1.3:conf:ags/LTI-AGS-2
    • AGS line-item mappings carry the label and scoreMaximum of the gradebook column the tool publishes to.
  • lti:1.3:conf:ags/LTI-AGS-3
    • Interop-proven: createLineItem POSTs a line item carrying the launch resourceLinkId to the platform’s line-item container over an OAuth token, and the platform echoes the bound id back — the lane asserts the resourceLinkId on the recorded request.
  • lti:1.3:conf:ags/LTI-AGS-4
    • Interop-proven: the adapter’s listLineItems reads the platform’s line-item container over an OAuth lineitem.readonly token, narrowed by resource_link_id / resource_id / tag — the lane asserts the filter round-trips on the recorded GET and the parsed gradebook columns come back. (Wired through @lti-tool/core’s LTITool.listLineItems; emergent maps the result to LtiToolAgsLineItem[].)
  • lti:1.3:conf:ags/LTI-AGS-5
    • emergent publishes scores via the AGS publication flush; the Score carries userId, activityProgress, gradingProgress and timestamp.
  • lti:1.3:conf:ags/LTI-AGS-6
    • Interop-proven: submitScore POSTs a Score with scoreGiven on its scoreMaximum scale to the line item’s /scores endpoint over an OAuth token — the lane asserts both values on the recorded request.
  • lti:1.3:conf:ags/LTI-AGS-7
    • Interop-proven: the adapter’s readResults reads the line item’s AGS result container (GET …/results, application/vnd.ims.lis.v2.resultcontainer+json) over an OAuth token scoped result.readonly, and the lane asserts the returned result rows (userId, resultScore) and the authorized request.
  • lti:1.3:conf:ags/LTI-AGS-8
    • Interop-proven: the adapter performs full line-item management — createLineItem (POST), updateLineItem (PUT) and deleteLineItem (DELETE) against the platform line item over an OAuth token; the lane asserts the PUT body and the authorized PUT/DELETE requests.
  • lti:1.3:conf:core/LTI-CORE-1
    • The /lti/login route accepts the OIDC third-party login-init parameters and 302-redirects to the platform authorization endpoint via the adapter.
  • lti:1.3:conf:core/LTI-CORE-2
    • The /lti/launch route accepts the id_token + state and drives the launch. Interop-proven: the lti-interop lane signs a launch with the mock platform’s key and emergent’s adapter verifies the signature (against the platform JWKS), nonce, iss and aud end-to-end, and rejects a launch whose signature does not verify.
  • lti:1.3:conf:core/LTI-CORE-3
    • Interop-proven: a real signed launch resolves into a session carrying the resource link and context, and emergent maps the membership role claim into its internal role vocabulary.
  • lti:1.3:conf:core/LTI-CORE-4
    • Interop-proven: for an offering-mapped launch context the launch roles drive the NRPS roster sync end-to-end — the lane provisions an organization/offering/context-mapping and asserts the persisted NRPS context snapshot (memberCount > 0) after a real launch.
  • lti:1.3:conf:core/LTI-CORE-5
    • The login/launch routes reject missing required parameters and unsupported launch targets (boundary-enforced to the learner-web origin).
  • lti:1.3:conf:deep-linking/LTI-DL-1
    • Interop-proven: a real LtiDeepLinkingRequest carrying deep_linking_settings resolves into a Deep Linking session (isDeepLinkingAvailable) and emergent builds the auto-submitted return form to the request’s deep_link_return_url; a non-Deep-Linking (resource link) session is rejected.
  • lti:1.3:conf:deep-linking/LTI-DL-2
    • The DL response path is gated on the LtiDeepLinkingRequest message type — non-Deep-Linking sessions are rejected.
  • lti:1.3:conf:deep-linking/LTI-DL-3
    • emergent assembles the content_items claim honouring the platform’s deep_linking_settings accept_types and accept_multiple: it offers the launchable ltiResourceLink plus the link, html and standalone image representations, and enriches every link-like item with the offering’s canonical image (ADR-0035) as both thumbnail and icon. Interop-proven: the lti-interop lane drives a real Deep Linking launch advertising accept_types [“link”,“html”,“image”], decodes the signed response JWT and asserts emergent emits exactly those three with the canonical-image thumbnail (and the offline builder suite proves the accept_types filtering + fallback). The one content type deliberately not emitted is file: an offering’s only file is its Common Cartridge export, an authenticated org-admin workflow rather than a publicly fetchable URL, so emitting it would leave a broken link in the platform.
  • lti:1.3:conf:deep-linking/LTI-DL-4
    • The Deep Linking response route returns the auto-submitted-form HTML that redirects back to the platform’s deep_link_return_url.
  • lti:1.3:conf:nrps/LTI-NRPS-1
    • emergent consumes the NRPS claim from the launch and synchronizes the resolved roster into snapshots, memberships and grants.
  • lti:1.3:conf:nrps/LTI-NRPS-2
    • Interop-proven: the platform splits the roster across two pages and emergent follows the membership container’s Link: rel=next to read every page before syncing — the lane asserts emergent GETs page 2 over the OAuth bearer token and the persisted snapshot’s member_count spans both pages. Paging lives in the vendored @lti-tool/core (packages/lti-tool-core, NRPSService.getMembers walks rel=next; same fix proposed upstream on AntonOfTheWoods/lti-tool feat/nrps-rel-next-paging).
  • lti:1.3:conf:nrps/LTI-NRPS-3
    • Members carry their user_id (the launch sub) and roles; a member no longer active in the roster has its imported memberships/grants expired (status honoured).
  • lti:1.3:conf:nrps/LTI-NRPS-4
    • Interop-proven: the membership container read carries the bearer access token acquired for the contextmembership.readonly scope — the lane asserts the Authorization header on the recorded NRPS request.
  • lti:1.3:conf:platform-ags/PLAT-AGS-1
    • The launch carries the AGS endpoint claim (the lineitems container URL + granted scopes) and every AGS operation is scoped to the calling tool’s deployment in the context — a tool never sees another tool’s columns. Interop-proven: the claim is asserted and an undeployed tool is refused.
  • lti:1.3:conf:platform-ags/PLAT-AGS-2
    • createLineItemWithDb accepts line-item creation requiring a label (the gradebook-column title) and a scoreMaximum; the line item is a first-class gradebook column (not coerced into an Assessment).
  • lti:1.3:conf:platform-ags/PLAT-AGS-3
    • Each line item is assigned an id URL returned on every response, and a resourceLinkId binding is honoured (it must resolve to a resource link in the offering). Validated against conform-ed’s LineItemSchema.
  • lti:1.3:conf:platform-ags/PLAT-AGS-4
    • listLineItemsWithDb serves the line-item container as a paged array filterable by resource_link_id, resource_id and tag, media type application/vnd.ims.lis.v2.lineitemcontainer+json. Validated against conform-ed’s LineItemContainerSchema; the lane proves the resource_link_id filter round-trips.
  • lti:1.3:conf:platform-ags/PLAT-AGS-5
    • submitScoreWithDb accepts a score POSTed to the scores endpoint requiring userId, activityProgress, gradingProgress and timestamp; userId (the pairwise sub) is resolved back to the person, and an out-of-order score is rejected (last-writer-wins by timestamp).
  • lti:1.3:conf:platform-ags/PLAT-AGS-6
    • When an accepted score carries scoreGiven the platform requires scoreMaximum alongside it — the lane asserts a scoreGiven without scoreMaximum is rejected.
  • lti:1.3:conf:platform-ags/PLAT-AGS-7
    • listResultsWithDb serves the result container (GET …/results, filterable by user_id), each Result referencing its line item (scoreOf). Validated against conform-ed’s ResultContainerSchema.
  • lti:1.3:conf:platform-ags/PLAT-AGS-8
    • The full single-line-item REST surface is served — read/update/delete via GET/PUT/DELETE …/lineitems/{lineItemId}; the lane reads, PUT-replaces and deletes a line item end-to-end.
  • lti:1.3:conf:platform-core/PLAT-CORE-1
    • initiateResourceLinkLaunchWithDb mints a single-use login_hint + returns the OIDC third-party login-initiation parameters (iss, login_hint, target_link_uri, client_id, lti_deployment_id); completeAuthorizationWithDb validates the login_hint/client_id/redirect_uri and form-posts the signed id_token to the tool’s registered redirect_uri. Interop-proven: the mock tool initiates login and receives the launch at its redirect_uri.
  • lti:1.3:conf:platform-core/PLAT-CORE-2
    • signIdToken mints the id_token as an RS256 JWT with the platform key (env keypair, kid resolvable via the platform JWKS) carrying iss=platform issuer, aud=client_id, sub (the pairwise subject), nonce and iat/exp. Interop-proven: the mock tool verifies the signature against the platform JWKS plus iss/aud/nonce end-to-end.
  • lti:1.3:conf:platform-core/PLAT-CORE-3
    • buildResourceLinkClaims emits the required resource-link launch claims (message_type= LtiResourceLinkRequest, version=1.3.0, deployment_id, target_link_uri, resource_link). The normalized launch validates against conform-ed’s CoreLaunchRequestSchema in the lane.
  • lti:1.3:conf:platform-core/PLAT-CORE-4
    • The launch carries the roles claim — an array of LTI membership role URIs resolved from the person’s offering membership (mapMembershipRoleToLtiRoles) — binding the user to the context. Interop-proven: the lane asserts the learner role URI on the verified launch.
  • lti:1.3:conf:platform-core/PLAT-CORE-5
    • The platform issues only well-formed launches: the login_hint is single-use (replay-protected via consumed_at) and an unregistered redirect_uri is rejected — both asserted in the lane.
  • lti:1.3:conf:platform-deep-linking/PLAT-DL-1
    • initiateDeepLinkingLaunchWithDb + completeDeepLinkingAuthorization issue a LtiDeepLinkingRequest carrying the deep_linking_settings claim (deep_link_return_url, accept_types, accept_presentation_document_targets, accept_multiple, and a stateless HS256 data correlation token). The normalized request validates against conform-ed’s DeepLinkingRequestSchema.
  • lti:1.3:conf:platform-deep-linking/PLAT-DL-2
    • The request lti_message_type is LtiDeepLinkingRequest and the platform accepts the tool’s reply as a LtiDeepLinkingResponse (acceptDeepLinkingResponseWithDb) — both asserted in the lane.
  • lti:1.3:conf:platform-deep-linking/PLAT-DL-3
    • The platform verifies the tool’s signed response JWT against the tool’s JWKS (iss=client_id, aud=platform issuer) and validates content_items against conform-ed’s DeepLinkingResponseSchema; a wrong-key signature is rejected.
  • lti:1.3:conf:platform-deep-linking/PLAT-DL-4
    • The platform receives the response at the advertised deep_link_return_url (POST /lti/platform/deep-linking/return), re-derives the originating context from the verified data token, and persists each selected content item as a resource-link placement; a forged data token is rejected. The lane drives a three-item response and asserts three placements in the offering.
  • lti:1.3:conf:platform-dynamic-registration/PLAT-DR-1
    • buildPlatformOpenIdConfiguration serves the OpenID Connect openid-configuration document (GET /lti/platform/.well-known/openid-configuration) advertising the issuer, the authorization / token / JWKS / registration endpoints, RS256 + private_key_jwt, the service scopes, and the lti-platform-configuration capabilities claim (product_family_code + messages_supported). Asserted in the lane.
  • lti:1.3:conf:platform-dynamic-registration/PLAT-DR-2
    • registerToolViaDynamicRegistrationWithDb accepts a tool’s registration request at the registration endpoint bearing a registration access token, creates the catalog row with a platform-assigned client_id (registrationMode “dynamic”), and returns the registered client configuration + negotiated lti-tool-configuration. Interop-proven: a mock tool self-registers, is entitled to the initiating org with a minted deployment, and then obtains a private_key_jwt service token (a first-class client); a forged registration token is rejected.
  • lti:1.3:conf:platform-nrps/PLAT-NRPS-1
    • buildServiceClaims advertises the NRPS claim (context_memberships_url + service_versions [“2.0”]) in launches for the offering context. Interop-proven: the lane asserts the claim’s URL + versions.
  • lti:1.3:conf:platform-nrps/PLAT-NRPS-2
    • buildMembershipPageWithDb serves the membership container with media type application/vnd.ims.lti-nrps.v2.membershipcontainer+json and a Link: rel=next header when the roster pages. The normalized container validates against conform-ed’s MembershipContainerSchema.
  • lti:1.3:conf:platform-nrps/PLAT-NRPS-3
    • Each served member carries a user_id equal to the launch sub (the same per-(person,tool) pairwise subject, via the identity_binding) plus a roles array, and an inactive/withdrawn membership is surfaced with the matching status. Interop-proven: the lane asserts member.user_id === the launch sub.
  • lti:1.3:conf:platform-nrps/PLAT-NRPS-4
    • The membership route requires a Bearer access token bearing the contextmembership.readonly scope (and the tool must be deployed in the context); a token without it gets 403 and an undeployed tool is refused. The token is minted only via the private_key_jwt grant.
  • lti:1.3:conf:platform-proctoring/PLAT-PROC-1
    • completeProctoringAuthorization issues a LtiStartProctoring launch carrying the proctoring settings + start_assessment_url + a stateless HS256 session_data token, and the platform accepts the tool’s launch of the assessment (acceptStartAssessmentWithDb verifies the tool-signed StartAssessment vs its JWKS, validates it against conform-ed’s StartAssessmentMessageSchema, and advances the proctoring session to assessment_launched). The normalized StartProctoring validates against StartProctoringMessageSchema.
  • lti:1.3:conf:platform-proctoring/PLAT-PROC-2
    • acceptEndAssessmentWithDb accepts the tool’s LtiEndAssessment returning control at the end of the attempt (verified vs the tool JWKS, validated against conform-ed’s EndAssessmentMessageSchema), marking the session ended. Forged-session_data and wrong-key messages are rejected. The optional Assessment Control Service verdict is now ALSO honoured (ADR-0043): the platform advertises assessment_control_url in StartProctoring and accepts control.all-scoped AssessmentControl requests at POST /lti/platform/proctoring/control, recording every incident to lti_platform_proctoring_control_event and acting on the action — flag, pause/resume, extra_time, and terminate (which voids the in-flight native attempt). Interop-proven (flag → terminate, audit, wrong-tool reject).
  • lti:1.3:conf:platform-security/PLAT-SEC-1
    • The platform signs the id_token only with RS256 (asymmetric RSA) using its private key — the lane decodes the protected header and asserts alg=RS256 with the kid resolving in the JWKS.
  • lti:1.3:conf:platform-security/PLAT-SEC-3
    • The GET /lti/platform/jwks route exposes the platform’s public RS256 key (the kid of every issued launch resolves there) so a tool can verify the id_token signature. Interop-proven: the mock tool resolves the JWKS and verifies a real launch.
  • lti:1.3:conf:platform-security/PLAT-SEC-4
    • The platform operates a standalone OAuth 2.0 token endpoint (POST /lti/platform/token) accepting the client-credentials grant with private_key_jwt client authentication — issueServiceAccessTokenWithDb verifies the tool’s client_assertion against its registered JWKS and mints a scoped access token via the shared HS256 access-token codec. Interop-proven: a private_key_jwt grant yields a Bearer token, and an assertion signed by the wrong key is rejected.
  • lti:1.3:conf:proctoring/LTI-PROC-1
    • Interop-proven: emergent is the proctoring tool. handleStartProctoring verifies the LtiStartProctoring launch (the vendored @lti-tool/core accepts the message type + passes the lti-ap proctoring claims through), persists the proctoring session (lti_tool_proctoring_session), and — once setup completes — launches the assessment by returning a tool-signed StartAssessment auto-submit form to the platform’s start_assessment_url (POST /lti/proctoring/start). The lane decodes the emitted StartAssessment and asserts message_type=LtiStartAssessment, the platform’s session_data, and aud=platform issuer. The StartAssessment payload is validated against conform-ed’s StartAssessmentMessageSchema before signing.
  • lti:1.3:conf:proctoring/LTI-PROC-2
    • Interop-proven: at the end of the attempt endProctoredAttempt loads the proctoring session and returns control to the platform with a tool-signed EndAssessment auto-submit form posted to the session’s end_assessment_return_url (validated against conform-ed’s EndAssessmentMessageSchema, carrying the user’s roles). The lane decodes it and asserts message_type=LtiEndAssessment + deployment_id. The optional Assessment Control Service verdict is now ALSO honoured (ADR-0043): sendAssessmentControl obtains a control.all-scoped service token (client_credentials + private_key_jwt) and POSTs a conform-ed-validated AssessmentControlRequest to the platform’s advertised assessment_control_url, recording the outbound action + response in lti_tool_proctoring_control_event (authed POST /workflow/lti-tool/proctoring/sessions/:id/control).
  • lti:1.3:conf:security/LTI-SEC-1
    • Interop-proven: the adapter exposes the tool’s RS256 verification key (kty RSA, kid) via JWKS, and RS256 verification of a real platform-signed launch is exercised end-to-end by the same lane.
  • lti:1.3:conf:security/LTI-SEC-3
    • The /lti/jwks route serves the tool’s public JWKS (kid ‘main’) for the platform to verify the tool’s signed JWTs.
  • lti:1.3:conf:security/LTI-SEC-4
    • Interop-proven: on launch, emergent acquires an OAuth 2.0 client-credentials access token from the platform token endpoint with a signed client_assertion, then makes the NRPS service call authorized by that bearer token — the lane asserts both on the recorded platform requests.