Skip to content
aoppenh1-afkPublic

About

macOS lecture recorder: local transcription and notes

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

203 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lectern

Lectern is a macOS app for students. It records lectures and shiurim from a microphone, Zoom, or other system audio on your Mac. It transcribes English and mixed English-Hebrew audio and turns it into nested outline notes, flashcards, and quizzes. You can also sync Canvas courses, deadlines, and grades, export notes to Google Docs, and chat with your course documents.

Lectern launches Google's official Antigravity ACP agent on your Mac with a separate Google sign-in. Codex and OpenCode are also supported. Your library stays on your Mac. AI processing sends content to the selected provider; optional Google Docs export and hosted sharing send the content you choose to those services.

Download the latest release or follow the illustrated installation guide.

Requirements

  • macOS 26 or later, Apple silicon
  • Xcode 26 (full Xcode, not only Command Line Tools) to build from source
  • A Google account with access to Antigravity
  • Optional: a Canvas personal access token; a Google account for Google Docs

Install from a release

  1. Follow the illustrated installation guide, or download Lectern-<version>.dmg from the latest release.

  2. Open the DMG and drag Lectern onto the Applications folder in the installer window. Wait for copying to finish, then eject the installer. ZIP downloads are also available: unzip and drag Lectern.app into Applications.

  3. If macOS blocks the downloaded app, clear its download quarantine. Releases use a self-signed certificate and are not notarized:

    xattr -dr com.apple.quarantine /Applications/Lectern.app
    open /Applications/Lectern.app

    You can also do this from System Settings › Privacy & Security › Open Anyway. In-app updates do not use the browser download flow.

  4. Complete the setup assistant:

    • Antigravity (required). Click Install Antigravity so Lectern can download and verify Google's official ACP runtime, then click Sign in with Google. The normal agy CLI is not required and its sign-in is intentionally separate.
    • Canvas (optional). School Canvas address plus a personal access token (Canvas › Account › Settings › Approved Integrations › New Access Token). Stored in Keychain.
    • Google Docs (optional). Click Connect Google Docs and approve access in your browser. The distributed app must include Lectern’s shared OAuth client.

Rerun the assistant from Settings › General.

Updating

Lectern checks this repository's GitHub Releases about once a day and offers to install a newer build in place (Settings › General › Check for updates, or Lectern menu › Check for Updates…). It downloads the zip, verifies the checksum, swaps the app, and relaunches.

Settings › General › Update channel switches tracks: Stable follows tested releases, published only when a new release is cut; Dev follows every commit on main via prereleases and may break. Switching back to Stable offers the latest stable release as the way out.

You can also download any version from the Releases page.

Canvas Zoom reminders

While Lectern is running, connected Canvas accounts are checked about every five minutes for Zoom invitations in announcements and full Inbox threads. Lectern reads the [Create Meeting] block's main Time: field and ignores the trailing recurrence dates. Eastern and other supported time zones use daylight saving rules.

A matching Canvas calendar event, or an explicit syllabus line such as “Class meetings: Wednesdays 6:45 PM,” can override an early invitation time. The match must identify the same course and weekday/date, with a start within one hour of the invitation. Ambiguous schedules keep the invitation time; arbitrary prose is not interpreted.

At the scheduled time, Lectern shows a join prompt when active or a macOS notification when in the background. If notifications are unavailable, it uses the prompt. Clicking Join Zoom opens the HTTPS link in your browser. The floating prompt stays available while you join, and recording starts only when you click Start Zoom recording when ready. Choose browser/system audio or Zoom app audio; the recording is filed under the matched course. Existing recordings are never interrupted.

Discovered reminders survive relaunches. New messages require Lectern to be running and online to sync; meetings more than 15 minutes past their start are skipped. The trailing date list does not create recurring reminders.

Course chat sources

Course chat keeps the selected documents in a separate local workspace for each course under ~/Library/Application Support/Lectern/Workspace/Courses. Codex and OpenCode receive file references and read relevant passages as needed. Follow-up questions reuse the live conversation instead of resending the documents and conversation history.

Antigravity uses native attachments, supplied once when a session starts. Changing the sources or agent settings, switching courses, clearing the chat, or restarting Lectern starts a fresh session. Saved conversation history restores continuity, and deselected documents are removed from that course's source folder on the next request. Agents still use tokens for passages they read and for conversation context; file references do not make those tokens free.

Other agents

Antigravity ACP is the default. Settings › Agents downloads its runtime and connects your Google account. For ChatGPT via codex-acp and OpenCode, the page provides official download instructions, installation in Terminal, and sign-in buttons. Click Refresh after installation. Custom agent commands are under Advanced.

With Default Lectern style selected, all three use the bundled Lectern notes skill, which contains separate layouts for English lectures and English-Hebrew shiurim. No separate skill installation or access to the author's notes is needed. Google Docs sync keeps headings and lists left to right and uses invisible left-to-right marks around Hebrew phrases so surrounding punctuation and numbers stay in the LTR sentence. The export format version is part of the sync hash, so the next sync can reformat existing notes without regenerating them.

Personal note styles

Settings › Notes lets you choose Default Lectern style or My notes style for future notes, including automatic shiur imports. In Generate, the Notes style picker can override that preference for one lecture.

To create a personal style, paste or import one to five examples of your notes, choose an installed agent, and click Create my style. Markdown, text, Word, RTF, and PDFs with selectable text are supported. Review the extracted text: Word and PDF extraction can lose formatting, and scanned PDFs need text first. Use representative excerpts totaling at most 60,000 characters.

The agent derives presentation instructions and short excerpts. Review or edit them, optionally generate a preview from Lectern's example lecture, then click Use my style. Save without switching retains the current preference and active style. Saved versions can restore an earlier revision. Selecting the default keeps the personal style available; Remove deletes its local samples and revisions.

Personal styles persist under ~/Library/Application Support/Lectern/Note Styles across app restarts and provider changes. Creating a style sends the samples to the selected provider. Future notes requests send the saved rules and short excerpts, with lecture content supplied separately. The default style sends no personal samples. Changing a style affects new generations; each running job keeps the revision it started with. No global agent skill installation is needed.

Read lectures from ChatGPT or Claude (MCP)

Open Settings → ChatGPT & Claude, sign in with Google, select courses, and click Start sharing. Lectern uploads only course names, lecture titles/dates, notes, and raw/cleaned transcripts to your hosted account. Changes sync about every 10 minutes while the app is open. The last synced copy remains readable when the Mac is closed. Removing a course takes effect after a successful sync.

Add https://lectern-app.vercel.app/mcp as a custom MCP connection in ChatGPT or Claude, choose OAuth authentication, and sign in with the same Google account. Enable Lectern in your conversation. The AI can browse courses, search lectures (including Hebrew), and read paged notes/transcripts with source line numbers. Account and workspace settings in the AI product may restrict custom connectors.

The Manage connections and shared data link opens /connect on the website. Disconnect individual apps there, or use Delete hosted library and disconnect all apps to delete the hosted copy and revoke access. Your Mac's original library is kept. Copies already retrieved by an AI service are governed by that service. Deleting from the Mac requires an online confirmation; a failed request does not claim that cloud access was revoked.

This replaces the former local server and private tunnel URLs. No tunnel or terminal setup is required for students. The site maintainer must provision the backend once before the connection works: see Hosted MCP deployment.

Run npm run test:hosted for the hosted database/OAuth/MCP integration tests and the normal Xcode scheme for native app checks. The hosted tests use an isolated embedded Postgres database and a fake Google identity provider; they do not upload real lecture content or substitute for testing real Google/ChatGPT/Claude accounts.

Privacy

  • Your local recordings, transcripts, and notes are stored in ~/Library/Application Support/Lectern. AI processing sends prompts and selected content to your chosen provider.
  • Google Docs export sends notes to your Google account. Optional hosted MCP sharing uploads selected courses, lecture metadata, notes, and transcripts so connected AI apps can read them. See hosted sharing for deletion and access controls.
  • Zoom / system-audio capture uses ScreenCaptureKit. macOS gates that behind Screen & System Audio Recording permission; Lectern discards the dummy video frames and stores only the WAV.
  • Canvas tokens, Google Docs OAuth state and transcription API keys are stored in the macOS Keychain. The official Antigravity ACP agent stores its own credential in a private Lectern profile.
  • Antigravity prompts and supported attachments are sent through ACP. Lecture audio uses ACP's native audio content block instead of a workspace path hint.
  • This repository does not contain credentials. Do not commit .env files or tokens.

Building from source

brew install xcodegen
git clone https://github.com/aoppenh1-afk/Lectern.git
cd Lectern
LECTERN_SIGN_IDENTITY=- scripts/install-local.sh

The command above explicitly uses ad-hoc signing for a local build. Maintainers with the original release certificate can omit LECTERN_SIGN_IDENTITY=-. Switching signing identities can require recording and Keychain permissions again.

project.yml is the source of truth. After you edit it, run xcodegen generate. Or open Lectern.xcodeproj in Xcode 26 and run the Lectern scheme.

xcodebuild -scheme Lectern -destination 'platform=macOS' test

See GitHub automation for CI checks, security scans, dependency updates, and the checks that still need a local run.

Install this repo for the user

Use this section when a person asks an AI agent (Codex, Claude, Cursor, and so on) to install Lectern from GitHub. Do the work on their Mac; do not hand them a list of steps unless a step requires their Google or Canvas sign-in.

  1. Confirm the machine is Apple silicon on macOS 26 or later, and that Xcode 26 is installed (xcodebuild -version). If Xcode is missing, install it from the App Store and run sudo xcodebuild -license accept and xcodebuild -runFirstLaunch before building.

  2. Install Homebrew if it is missing, then brew install xcodegen.

  3. Clone this repository and enter it:

    git clone https://github.com/aoppenh1-afk/Lectern.git
    cd Lectern
  4. Build the Release app and install it to /Applications. On the maintainer's Mac, scripts/install-local.sh uses the original release certificate. On another Mac without that certificate, explicitly choose ad-hoc signing for the local build:

    LECTERN_SIGN_IDENTITY=- scripts/install-local.sh

    Confirm /Applications/Lectern.app exists and defaults read /Applications/Lectern.app/Contents/Info.plist CFBundleShortVersionString prints a version.

  5. Clear Gatekeeper quarantine (the app is ad-hoc signed; there is no Developer ID):

    xattr -dr com.apple.quarantine /Applications/Lectern.app
    open /Applications/Lectern.app
  6. In Lectern's setup assistant, have the user click Install Antigravity. Lectern downloads the pinned official ACP runtime from Google, verifies its SHA-256 and identity, and keeps it under Application Support.

  7. Have the user click Sign in with Google and complete Google's browser flow. No Terminal command or normal Antigravity CLI installation is required.

Do not commit tokens, .env files, or Keychain material. Do not change git config. Do not force-push.

Publishing a release (maintainer)

Releases currently use a persistent self-signed certificate. This preserves the designated requirement, but does not prevent repeated Keychain prompts: macOS also checks a per-build code-hash partition for self-signed apps. A stable certificate alone is insufficient.

Builds and releases are pinned to the original Lectern Release Signing certificate fingerprint in scripts/release-signing.sh. On another Mac, import the existing .p12 backup into your login Keychain. Creating a new certificate with the same name does not preserve Keychain access. The setup helper is only for establishing a new identity, not restoring this app's release identity.

Stable releases can be published from any environment with repository workflow access:

gh workflow run stable.yml --ref main -f version=2.9 -f notes="What changed"

The manual workflow tests the app on a macOS runner, imports the original signing identity from the release-signing environment, and runs the release script below. Dispatch it on main with a version higher than the current version.

On a Mac with the original signing identity, run a release with:

scripts/release.sh 2.9 --notes "What changed"

The scripts refuse a missing or different certificate and verify the finished app against the pinned signing requirement before packaging. For a disposable local build only, use LECTERN_SIGN_IDENTITY=- scripts/build-app.sh. Such builds may require Keychain authorization again.

Dev releases also require the original certificate. GitHub Actions needs two secrets in the release-signing environment: LECTERN_SIGNING_P12_BASE64, the base64-encoded .p12 containing the original certificate and private key, and LECTERN_SIGNING_P12_PASSWORD, that archive's password. Restrict the environment to the main branch. The dev workflow imports it into a temporary keychain, verifies its fingerprint, and removes the keychain after publishing. Missing secrets or a different certificate stop the release rather than publishing an ad-hoc build. Keep these values out of repository files, Actions variables, and logs. Public readers and fork pull requests cannot access the secrets; code running in an authorized release job can, so review changes to release workflows and build scripts before merging.

Ad-hoc builds change their designated requirement on every rebuild, so macOS treats each update as a different app for Screen & System Audio Recording permission. Signing every stable and dev update with the original certificate removes this identity change without a paid Apple Developer account. After switching from an ad-hoc build, grant recording permission once for the signed app and restart Lectern. Verify retention on the next signed update; this does not suppress macOS's own periodic consent prompts or resolve the separate Keychain behavior described below. See Apple's explanation of TCC and ad-hoc signing and code signing requirements.

With self-signed releases, Always Allow may need to be repeated after each rebuild. Apple Development signing can provide a stable team partition for personal development builds; Developer ID is the signing route for public distribution. Neither eliminates prompts when the login Keychain itself is locked.

scripts/release.sh verifies the signing identity, bumps MARKETING_VERSION / CURRENT_PROJECT_VERSION in project.yml, builds and signs the app bundle with the persistent certificate, packages the ZIP and DMG, commits, tags the requested version, pushes, and creates the GitHub release with both archives and their .sha256 files. Installed copies pick it up on their next check.

Google Docs release configuration

Users connect their Google account in Settings → Google Docs. Lectern requests only https://www.googleapis.com/auth/drive.file, with no full-Docs, full-Drive, email, or profile scope. Google Docs supports this scope for creating documents, reading their tabs, and updating them. Tokens remain in the macOS Keychain. AppAuth uses the system browser, a loopback redirect, and PKCE. No hosted OAuth backend is needed.

The app maintainer must configure one shared Google Cloud project before distributing this feature:

  1. Enable Google Docs API and create an OAuth client of type Desktop app.
  2. Configure the external audience, Lectern branding, support contact, homepage and privacy policy. List only drive.file under Data Access. Remove the old documents scope from the consent configuration.
  3. Move the audience to In production for distribution. Complete any brand verification Google requests. Testing mode still limits users and can show warnings; changing code does not publish or verify the Google project.
  4. Supply LECTERN_GOOGLE_CLIENT_ID and LECTERN_GOOGLE_CLIENT_SECRET as Xcode build settings, or export both variables before running scripts/build-app.sh. Use the downloaded Desktop client values. They identify a public desktop client and are embedded in Info.plist, so they are extractable from the app. Never use a web-server client secret here or treat these values as confidential credentials.
  5. Test a release build with a separate Google account: connect, push two lectures in one course, edit and push one again, restart the app and push again. Confirm one document with two tabs, updated content, and no unexpected consent warning.

Builds without a client show an unavailable message instead of credential fields. Existing sessions from a different client or with broader requested scopes require reconnection. Local sign-out does not revoke old Google grants; users can remove those in their Google Account connections. A new Google project may not be able to read documents created by the old client. If Google returns 404, Lectern leaves the old document intact and creates a replacement on the next push. Other API errors remain visible instead of silently creating duplicates.

Google lists drive.file as non-sensitive, so it avoids sensitive/restricted scope review. This does not guarantee that every account or project will show no warning. Standard Docs API use is currently available at no additional cost, subject to Google’s quotas. Google currently plans charges for exceeding those quotas later in 2026, so this is not a promise of unlimited free use. See Docs scopes, usage limits, and brand verification.

DMG packaging

Releases publish a drag-to-Applications DMG for new installs and keep the ZIP plus checksum for in-app updates. scripts/package-dmg.sh /path/to/Lectern.app /path/to/Lectern-version.dmg packages an existing signed app without rebuilding it. It requires macOS and Python 3 with venv support, and installs the pinned packaging dependencies from scripts/dmg-requirements.txt into the ignored dist/.dmg-tools environment.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

macOS lecture recorder: local transcription and notes

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages