pencil.nu is a Northwestern course-planning browser extension for CAESAR and Paper.
Go to pencil.nu for the current install link, release notes, and student-facing setup instructions.
This repository is mainly here for transparency and review. If you just want to use pencil.nu, start at pencil.nu, install the browser extension, then open CAESAR or Paper as usual.
- Better CAESAR class search powered by Paper catalog data, with live CAESAR status fetched on demand.
- Seats, notes, requirements, attributes, and section details in CAESAR shopping-cart and class-search views.
- CTEC links and summaries for matching courses and instructors.
- Paper schedule enhancements that show CTEC summaries and analytics alongside schedule cards.
- Enrollment navigation improvements, including term switching and smoother redirects between CAESAR enrollment pages.
pencil.nu is a client-side browser extension. It does not run a pencil.nu-owned backend that receives student CAESAR data, and it does not add third-party analytics or remote logging in the current codebase.
Sensitive Northwestern session data stays within the browser and Northwestern services:
- CAESAR and CTEC requests are made from the installed browser extension using the student's existing authenticated browser session.
- CAESAR form state, PeopleSoft tokens, cookies, and CTEC page responses are not posted to pencil.nu-owned servers.
- Local extension state is stored in
chrome.storage.local,localStorage, orsessionStorageon the device. This includes feature toggles, course/catalog caches, CTEC caches, seat/note caches, rate-limit timestamps, and the local staged-rollout gate data. - The staged-rollout gate reads the student's CAESAR profile name only to determine rollout eligibility by last-name bucket or local access code. That value is stored locally and is not transmitted to a pencil.nu service.
- Paper catalog data is fetched from Paper data endpoints (
api-legacy.dilanxd.comandcdn.dil.sh) and cached locally. Those requests are for catalog/course data, not CAESAR credentials or CAESAR form submissions.
The requested permissions are scoped to the product surface:
storageandunlimitedStorage: local settings and local caches.declarativeNetRequestWithHostAccess: optional redirect fromcaesar.northwestern.edutocaesar.ent.northwestern.edu.- Host access for CAESAR, Paper, Northwestern SSO, Northwestern Bluera/CTEC, and Paper catalog/CDN endpoints needed by the features above.
Why this should not be problematic for review:
- It is source-available and auditable from this repo.
- It is Manifest V3 and builds to a normal Chrome extension package.
- It augments pages the student already has permission to view.
- It keeps pencil.nu-specific state local to the browser rather than creating a separate student-data store.
- It can be deployed first to a small Google Admin test group or OU before any broad rollout.
- NUIT can disable or force-install it using standard Chrome enterprise app and extension policy controls.
npm install
npm run typecheck
npm run buildBuild only Chrome:
npm run build:chromeBuild only Firefox:
npm run build:firefoxChrome output is written to dist/chrome. Firefox output is written to dist/firefox.
Chrome:
- Run
npm run build:chrome. - Open
chrome://extensions. - Enable Developer Mode.
- Click
Load unpacked. - Select
dist/chrome.
Firefox:
- Run
npm run build:firefox. - Open
about:debugging#/runtime/this-firefox. - Click
Load Temporary Add-on. - Select
dist/firefox/manifest.json.
The preferred managed deployment path is Chrome Web Store or private/domain Chrome Web Store distribution, then Google Admin console policy. Google documents enterprise publishing, private domain publishing, allowlisting, and force-install controls in the Chrome Enterprise publishing options and Chrome Admin apps and extensions docs.
Recommended Chrome Web Store or private-domain flow:
-
Update
versioninsrc/manifest.base.jsonfor the release. -
Run
npm install,npm run typecheck, andnpm run build:chrome. -
Zip the contents of
dist/chrome, not the parent folder:cd dist/chrome zip -r ../../pencil-chrome.zip .
-
Upload
pencil-chrome.zipin the Chrome Web Store Developer Dashboard. -
Choose the appropriate visibility: public, unlisted, trusted testers, or private/domain publishing.
-
After review/publication, provide NUIT the Chrome Web Store extension ID.
-
In Google Admin, go to
Devices > Chrome > Apps & extensions > Users & browsers. -
Select the test organizational unit or group first.
-
Add the extension from the Chrome Web Store or by extension ID.
-
Accept the listed permissions on behalf of the organization.
-
Set the installation policy to
Allow install,Force install, orForce install + pindepending on the rollout plan. -
Verify on a managed test device at
chrome://policy, then broaden the OU/group only after the behavior is confirmed.
Self-hosted CRX flow if NUIT does not want Web Store distribution:
- Run
npm run build:chrome. - Open
chrome://extensions, enable Developer Mode, and clickPack extension. - Use
dist/chromeas the extension root directory. - For the first package, leave the private key field empty. Chrome creates a
.crxand.pem. - Store the
.pemsecurely. The same key is required for future updates so the extension ID remains stable. - Host the
.crxon HTTPS. - Host an update manifest XML file whose
codebasepoints to the.crxand whoseversionmatchesmanifest.json. - For every update, increment
src/manifest.base.json, rebuild, repack with the same.pem, update the hosted.crx, and update the XML version. - In Google Admin, add the extension by ID from a custom URL, using the update manifest URL required by Chrome policy.
- Test in a small OU/group before force-installing more broadly.
Chrome's package/update documentation covers signed CRX packaging, private keys, update manifests, and update URLs in more detail:
- Update your Chrome Web Store item
- Self-host Chrome extensions
- Automatically install apps and extensions
This repo uses one TypeScript WebExtension codebase with browser-specific output:
dist/chrome: Chrome/Chromium Manifest V3 service worker build.dist/firefox: Firefox-compatible build with background scripts.
Core pieces:
src/manifest.base.json: shared extension manifest, host permissions, and content-script matches.scripts/build.mjs: esbuild bundle plus browser-specific manifest generation.src/background.ts: background fetch bridge, auth popup tracking, and CAESAR domain redirect rule.src/content/index.ts: content-script entrypoint. It starts the access gate, popup messaging, early term mask, and augmentation runner.src/content/framework/*: reusable augmentation lifecycle and DOM mutation runner.src/content/augmentations/*: independent feature modules for class search, seats/notes, CTEC links, Paper CTEC UI, and enrollment navigation.src/content/peoplesoft/*: shared PeopleSoft request orchestration, traffic locking, form-state serialization, and parsing helpers.src/popup/*: extension popup UI for feature toggles, local cache controls, and access status.src/shared/*: typed message contracts between content scripts, popup, and background worker.
At page load, Chrome injects pencil.nu's content script only into the page hosts listed under content_scripts in src/manifest.base.json: CAESAR and Paper. Host permissions separately allow the background worker to fetch required Northwestern SSO/Bluera/CTEC pages and Paper catalog/CDN data.
The content script starts a registry of independent augmentations. Each augmentation decides whether it applies to the current page, finds the relevant DOM nodes, fetches any needed data, and renders a small UI enhancement into the existing CAESAR or Paper page.
For CAESAR pages, pencil.nu reads the current PeopleSoft form state from the active page and sends normal CAESAR requests with the same authenticated browser session the student is already using. It parses the returned HTML locally and renders class status, seats, notes, enrollment metadata, or CTEC links.
For Paper pages, pencil.nu uses Paper catalog data for course search and schedule context. When CTEC data is requested, the extension uses the student's Northwestern session to read the relevant Northwestern CTEC/Bluera pages, parses summaries locally, and stores only local cache entries in the browser.
The background worker does not maintain a server-side data store. It only handles extension tasks that need background privileges: cross-origin fetches allowed by the manifest, opening/closing the Northwestern auth popup, and maintaining the optional CAESAR short-domain redirect rule.
Local caches exist to reduce repeat CAESAR/CTEC/Paper requests and to keep the UI fast. Students or admins can clear extension storage through Chrome extension settings or browser storage controls.
pencil.nu is released under the MIT License. See LICENSE.
