@prairielearn/respondus-lockdown-browser
v0.1.2
Published
PrairieLearn's Respondus LockDown Browser integration library. The implementation is shipped as a JWE-encrypted bundle (RSA-OAEP-256 + A256GCM); only PrairieLearn holds the private key.
Readme
@prairielearn/respondus-lockdown-browser
PrairieLearn's Respondus LockDown Browser integration library.
The implementation is shipped as a compact JWE (RFC 7516) blob at
dist/library.jwe, encrypted with alg: RSA-OAEP-256 + enc: A256GCM.
Only PrairieLearn holds the private key that can decrypt it. The loader in
this package (dist/loader.js) is a thin wrapper around
jose's compactDecrypt plus a sandboxed
vm evaluation step — small enough to audit top-to-bottom.
Install
npm install @prairielearn/respondus-lockdown-browserUsage
import { loadLibrary } from '@prairielearn/respondus-lockdown-browser';
import type { Library } from '@prairielearn/respondus-lockdown-browser/types';
const library: Library = await loadLibrary({
keys: {
'pl-2026-03-a8f1c9': process.env.RESPONDUS_PRIVATE_KEY_PEM_Q1!,
'pl-2026-07-b4d2e7': process.env.RESPONDUS_PRIVATE_KEY_PEM_Q3!,
},
anchorUrl: import.meta.url,
});keys is a map from kid (key ID) to PEM PKCS#8 private key. The loader
reads the kid out of the packaged JWE's protected header and picks the
matching PEM. If the kid isn't in the map, the loader throws naming both
the expected kid and the ones the caller passed — no trial decrypts, no
silent "decrypt failed" mystery at 2 AM.
During key rotation keep both the old and the new kid in the map until every deploy has moved to a package version encrypted to the new kid.
anchorUrl is the caller's own import.meta.url (or any file URL that
points into the host project). It tells the sandboxed VM where to resolve
bare specifiers from, so the bundle's require(...) / dynamic import(...)
walks up to the host's node_modules, not this package's install path.
Keys
Each keypair is a PEM PKCS#8 RSA-2048 (or larger) key:
openssl genrsa -out priv.pem 2048
openssl rsa -in priv.pem -pubout -out pub.pemThe publisher's GitHub Actions repo variables:
LIBRARY_PUBLIC_KEY_PEM— contents ofpub.pemLIBRARY_PUBLIC_KEY_KID— opaque string identifying the keypair (PL convention:pl-YYYY-MM-<hex6>; nothing in this package parses it)
The private half stays on the consumer side, keyed by the same kid.
Dev mode
PrairieLearn only, requires the plaintext bundle (which is never published):
await loadLibrary({
sourcePath: '/path/to/respondus-lockdown-browser/dist/library.js',
anchorUrl: import.meta.url,
});sourcePath and keys are mutually exclusive.
License
Proprietary. See LICENSE.
