lectern

A complete, plain-language walkthrough of Lectern, from installing the app to running multiple accounts. It is written for everyday users — no prior knowledge of Farcaster, crypto keys, or the command line is assumed. Technical terms are defined the first time they appear, and most sections include a diagram or an example screen.

Every section is tagged with where the feature lives:

  • 🖥️ App — works in the macOS app
  • ⌨️ CLI — works in the lectern command-line tool
  • Both — the same feature on both surfaces

Table of contents

  1. First 5 minutes with Lectern
  2. What Lectern is
  3. Install
  4. Create your first account
  5. The macOS app at a glance
  6. Reading
  7. Writing
  8. Profile, identity, verifications
  9. Direct casts and XMTP
  10. Spaces
  11. Cashtags + token references
  12. Mini apps and wallet
  13. Tools
  14. Lectern Pro: trial and subscription
  15. Recovery and backup
  16. Multi-account management (CLI)
  17. Two-factor auth (CLI)
  18. Bookmarks
  19. Snaps
  20. Troubleshooting
  21. CLI reference

First 5 minutes with Lectern

Welcome to Lectern. The first time you reach the main window, a short six-step welcome tutorial runs automatically — it introduces the layout, highlights the features most relevant to where you are coming from, and walks you through your first post. You can reopen it at any time from the macOS menu bar under Help → Welcome Tutorial (⌘?).

This guide is the complete companion to that tutorial. Two brief orientations follow — read whichever describes you — and then continue to Install.

A note on names. The app once called "Warpcast" is now Farcaster (farcaster.xyz), the network's official client. Where older material says "Warpcast," it means farcaster.xyz. This guide uses farcaster.xyz throughout.

Coming from farcaster.xyz

It is the same network and the same account — your FID (your permanent Farcaster account number) and your posts, follows, and verified wallets all come with you. What changes is ownership and billing: Lectern moves the keys that control your account out of Farcaster's custody and into your Mac, and it bills its paid tier on-chain rather than through the App Store.

  farcaster.xyz                     Lectern
  ─────────────────────────────────────────────────────────────
  Keys held by Farcaster      →     Keys held in your Mac's Keychain
  Pro billed via App Store    →     Pro billed on-chain (6.9 USDC/mo)
  Free posts capped at 320 b  →     Long posts to 1,024 bytes, free
  One account at a time       →     Switch accounts with ⌘1–⌘9
  Farcaster DMs only          →     Farcaster + XMTP DMs in one inbox
  ─────────────────────────────────────────────────────────────

Beyond the table, Lectern adds tools farcaster.xyz does not ship — Snap Maker, Airdrop, Cast Analytics, and Follow Manager — plus scheduled posts that send while the app is closed, a private on-disk archive of everything you publish, and no telemetry of any kind. Nothing leaves your Mac except the posts you choose to publish. And because your keys stay in your Keychain, you can move to any other Farcaster client whenever you like; nothing locks you in.

New to Farcaster

Farcaster is a social network built as an open protocol — a shared public standard rather than one company's private app. Your account is a set of cryptographic keys that you own, and any compatible app can display your posts. Lectern is one such app. A few things worth knowing on day one:

  • Your account is a 24-word recovery phrase. During setup, Lectern shows you a list of 24 words that can restore your entire account. Write them on paper and keep them somewhere safe. If you lose them, you lose the account.
  • Posts are public and lasting. A post (called a cast) can be deleted from your own machine, but copies persist on the network's public servers. Treat every cast as permanent and public.
  • Most features are free. The paid tier, Lectern Pro, mainly adds authoring tools; everything you need to read, post, message, and manage your account is free.

Five things to try first

Once you have finished setting up an account (the next two sections), these five actions give you a feel for the app:

flowchart LR
    A[Press ⌘N<br/>write a post] --> B[Press ⌘⇧B<br/>save a bookmark]
    B --> C[Open Wallet<br/>receive or send]
    C --> D[Install the<br/>background scheduler]
    D --> E[Start the<br/>7-day Pro trial]
  1. Press ⌘N to write a post. Links automatically become rich previews, and typing @username turns into a tappable mention.
  2. Press ⌘⇧B to open Bookmarks. Save any post from its right-click menu, then tag and search it later.
  3. Open Wallet from the sidebar's TOOLS section — show your address with a QR code, send a token, or swap one token for another.
  4. Open Settings → Developer and install the background scheduler, so scheduled posts send even when Lectern is closed.
  5. Open Settings → License and choose Start 7-day trial to unlock every Pro feature for a week, free.

What Lectern is

🌐 Coming from farcaster.xyz: It is the same network and the same account. Lectern simply moves the keys that control your account out of Farcaster's hands and into your Mac. Sign-in with Farcaster still works on sites that support it. Lectern Pro is billed on-chain in USDC rather than through the App Store.

Lectern is a Farcaster client for macOS — a desktop application for reading and posting to the Farcaster network. It is built around a single principle: you, and only you, hold the keys to your account.

A key here means a secret code that proves you own your account, much like the master password to a safe. In most social apps the company holds that secret on its servers. In Lectern, the keys are stored only in your Mac's Keychain — the secure, encrypted vault macOS already uses for your saved passwords. Lectern never sees your keys, never copies them to a server, and never connects anywhere to "check in." This is what is meant by self-custody: the account is yours to keep.

Lectern offers two ways to work, and they stay perfectly in step with each other:

  • The macOS app 🖥️ — a full graphical application with a feed, notifications, a profile editor, and messaging.
  • The lectern command-line tool ⌨️ — a text-based tool for people comfortable typing commands, useful for automation and advanced control.

Anything you set up in one immediately appears in the other, because both run on the same underlying engine.

Consider a new user, Priya, deciding whether Lectern fits her needs. The summary below gives her the essentials at a glance.

  LECTERN — AT A GLANCE
  ─────────────────────────────────────────────────────────
  Platform      macOS app  +  command-line tool (desktop only)
  Your keys     stored only in your Mac's Keychain — never sent anywhere
  Included      live feed · notifications · profile editor ·
                direct messages · mini apps with wallet · block + mute ·
                post authoring · analytics · multiple accounts
  Not included  no iPhone or iPad app
  Safety        every post is confirmed before it is sent —
                with Touch ID in the app, or an explicit flag on the
                command line
  ─────────────────────────────────────────────────────────

A built-in safety net. Lectern is deliberate about anything that posts publicly or moves money. In the macOS app, every post and every wallet transaction asks you to confirm with Touch ID first. On the command line, commands that publish or spend never do so by default — you must add an explicit instruction to go ahead. Nothing leaves your Mac until you have approved it.

flowchart LR
    A[You write a post] --> B{Confirm before sending}
    B -->|App: Touch ID| C[Posted to Farcaster]
    B -->|Command line: explicit flag| C
    B -->|No confirmation| D[Nothing is sent · stays a draft]

Install

Lectern comes in two parts: the macOS app for everyday use, and the optional lectern command-line tool for advanced control. Most people only need the app. Both are contained in a single download.

Requirement

  • macOS 14 (Sonoma) or newer. Lectern does not run on Windows, Linux, iPhone, or iPad.
  REQUIREMENT CHECK
  ─────────────────────────────────
  macOS 14 or newer        required
  Windows / Linux          unsupported
  iPhone / iPad            unsupported
  ─────────────────────────────────
  To check your version:  Apple menu  >  About This Mac

Install the macOS app 🖥️ App

The app is delivered as a DMG — a disk-image file, the standard way Mac apps are distributed. Opening it presents the app so you can drag it into your Applications folder.

  1. Sign in (or sign up) at lctrn.app with your email — you get a one-time magic link, no password. From your dashboard, choose pay on-chain or the free trial; either way Lectern emails you a personal, time-limited download link. (The link is unique to you and expires in 24 hours; there is no public direct-download URL.)
  2. Open the downloaded DMG, then drag Lectern.app onto the Applications folder shown beside it.
  3. Launch Lectern from your Applications folder. You can also press ⌘ Space, type "Lectern," and press Return.

On first launch, macOS asks for permission for Lectern to use your Keychain. This is expected: it is how Lectern reaches the secure vault where your keys are stored. Click Always Allow so you are not asked again on later launches.

flowchart TD
    A[Open the Lectern DMG] --> B[Drag Lectern.app to Applications]
    B --> C[Launch Lectern from Applications]
    C --> D[macOS asks for Keychain access]
    D --> E[Click Always Allow]
    E --> F[Lectern is ready to use]

Install the command-line tool ⌨️ CLI

The lectern command-line tool is for people who prefer typing commands or want to automate tasks. It ships inside the app you already installed, so there is nothing extra to download — you only make it easy to run by name.

Consider Theo, a developer who wants to script his posting. He runs the command below once to make lectern available everywhere in his terminal:

ln -sf /Applications/Lectern.app/Contents/Helpers/lectern /opt/homebrew/bin/lectern

Prefer a personal location instead of the system one? Use this:

ln -sf /Applications/Lectern.app/Contents/Helpers/lectern ~/.local/bin/lectern

He then confirms it works:

$ lectern --version
0.2.0

$ lectern doctor

doctor checks that Lectern can reach the Farcaster network's on-chain components through your internet connection. A ✗ on a fresh install is usually harmless — the app uses the same settings — but it is worth noting if you intend to rely on the command-line tool for account setup.

  $ lectern --version
  0.2.0

  $ lectern doctor
  RPC: https://mainnet.optimism.io

  ✓  IdRegistry              0x00000000Fc6c5F01Fc30151999387Bb99A9f489b  bytecode=12345 bytes
  ✓  KeyRegistry             0x00000000Fc1237824fb747aBDE0FF18990E59b7e  bytecode=10456 bytes
  ✓  StorageRegistry         0x00000000fcCe7f938e7aE6D3c335bD6a1a7c593D  bytecode=9876 bytes

doctor verifies that each hardcoded Farcaster contract on OP Mainnet has bytecode at its address (reached through your RPC). It prints one / line per contract; on failure it ends with a note that one or more contracts failed verification.

Optional: your own network connection ⌨️ CLI

This step is only relevant if you plan to create or recover an account from the command line. The macOS app needs none of it.

Certain command-line operations talk directly to the Optimism network (where Farcaster accounts are registered). For these, you may supply your own RPC connection — a private link to the network, available free from providers such as Alchemy. Create the file below only if you intend to register or recover an account from the command line:

Create ~/Library/Application Support/Lectern/config.env:

LECTERN_OP_RPC=https://opt-mainnet.g.alchemy.com/v2/<your-key>
LECTERN_SOL_RPC=https://api.mainnet-beta.solana.com

LECTERN_OP_RPC is the only line most people ever need; LECTERN_SOL_RPC matters only if you verify a Solana wallet address. If you set neither, Lectern uses free public connections — fine for reading, but occasionally slow under heavy load.

Background scheduler — for posts that send while the app is closed 🖥️ App

By default, a scheduled post is sent only while Lectern.app is open. If you want scheduled posts to go out even when the app is closed, install the optional background helper.

  1. Open the app.
  2. Go to Settings → Developer → Background Scheduler → Install.
  3. macOS shows a notification confirming Lectern may run in the background. It now appears in System Settings → General → Login Items & Extensions under "Allow in the Background."
  4. If macOS marks the helper as needing approval, the Install button explains this and opens System Settings so you can switch Lectern on.

The first install may ask once for your Mac administrator password. This is a one-time security upgrade that lets the background helper read your signing key without prompting at send time. After this single prompt, scheduled posts send silently. (See Troubleshooting → Keychain ACL migration for the full explanation.)

To remove it later, return to the same panel and choose Uninstall. Scheduled posts then revert to sending only while the app is open.

flowchart TD
    A[Settings → Developer → Background Scheduler] --> B[Click Install]
    B --> C[macOS confirms background permission]
    C --> D{macOS asks for admin password?}
    D -->|Yes, once| E[Enter password · one-time setup]
    D -->|No| F[Setup complete]
    E --> F
    F --> G[Scheduled posts send even when the app is closed]

Create your first account

There are two ways to begin, and the right one depends on whether you already have a Farcaster account:

  • In the macOS app 🖥️ — recommended for nearly everyone. The app's setup wizard handles the entire technical process for you.
  • On the command line ⌨️ — for advanced users who want to run each step themselves.

Before going further, three plain-language terms will appear repeatedly:

  • Your FID (Farcaster ID) is your account's permanent identity number on the network — the equivalent of a customer number that never changes.
  • Your fname (Farcaster name) is your public username, the @handle people see and type.
  • A custody key is the master secret that owns your account; a recovery key is a backup secret that can rescue the account if the custody key is ever compromised. Both live only in your Mac's Keychain.
flowchart TD
    A[Do you already have a Farcaster account?] -->|No — brand new| B[Create a new account]
    A -->|Yes — moving from another app| C[Import your existing account]
    B --> D[Use the macOS app wizard · recommended]
    B --> E[Use the command line · advanced]
    C --> F[See: Recovery and backup]

The macOS app does the technical work for you. You provide a name and a small amount of funds; the setup wizard performs the on-chain registration, reserves storage, sets up your signing key, and prepares sign-in — all automatically, with progress shown at each step.

Consider Theo, who is new to Farcaster. His path looks like this:

  1. Open onboarding and choose "I'm new to Farcaster."
  2. Pick a local name — a private label for the keys on your Mac, not your public username. The rules are simple: lowercase letters, numbers, and dashes, 1 to 16 characters.
  3. Generate keys. Lectern creates your custody key and recovery key and shows you a recovery phrase — a list of 24 ordinary words that together can restore your entire account on any device. Write these 24 words down on paper and store them somewhere safe. Never share this phrase with anyone and never type it into a website — anyone who has it controls your account. Lectern keeps a copy in your Keychain, but the paper copy is your only backup if the Mac is ever lost.
  4. Fund the custody address. The wizard displays an address and the exact amount to send — it computes this from the live registration + storage prices plus a small gas buffer (roughly $1 in OP ETH). Send at least the amount shown, on Optimism. Any unused amount simply stays in your account. The wizard watches for the deposit and advances on its own once it arrives.
  5. Let the wizard finish. It now runs the required steps in order — Register FID, Rent storage (a small allotment Farcaster requires before you can post), Register signer (the per-app key that lets this Mac post on your behalf), and Register auth address (which enables Sign-in with Farcaster on supported sites). It then offers two optional steps you can complete now or later: Claim fname (your public @username) and Set profile (your display name, bio, and picture).

You do not run any of this yourself — you watch it complete.

sequenceDiagram
    participant You
    participant App as Lectern app
    participant OP as Optimism network
    You->>App: Choose "I'm new to Farcaster"
    You->>App: Pick a local name
    App->>App: Generate keys + show 24-word recovery phrase
    You->>App: Write phrase down on paper
    You->>OP: Send the amount the wizard shows (~$1 in OP ETH)
    App->>OP: Register FID · rent storage · register signer · register auth address
    App-->>You: Optional — claim fname + set profile
    App-->>You: Account ready

A note on what you will see. During key generation the app shows the 24 words in a numbered grid with a copy phrase button beside a warning banner. The banner is intentional. The phrase is the account itself — treat it with the same care as the keys to your home.

Creating an account on the command line — advanced ⌨️ CLI

The command-line path performs the same setup, but as a series of individual commands you run yourself. This suits advanced users and automation. A few commands send real transactions to the network, so Lectern is careful about which ones act immediately.

Important — the two flag conventions. register, register-signer, and claim-fname act for real by default; add --dry-run to preview them without sending. By contrast, cast and profile set only preview by default; you must add --send to publish. Read each command's output before assuming anything was broadcast.

1. Generate your keys. Theo starts here. Lectern creates the custody and recovery keys and displays the 24-word recovery phrase, then asks him to retype three of the words to confirm he has written them down.

$ lectern keys new theo
  ==================================================================
    RECOVERY PHRASE for `theo` — write this on PAPER, store offline
  ==================================================================

     1. abandon      2. ability    3. able       4. about
   ...                                                       (24 words)

  To confirm you wrote the phrase down, type the following 3 words:
    word #3:  able
    word #11: ...
    word #17: ...
  ✓ Confirmed. Recovery phrase verified.

  created account: theo
    custody   0x7f4d…   (m/44'/60'/0'/0/0)
    recovery  0x71db…   (m/44'/60'/0'/0/1)

The account name follows the same rule as before: lowercase letters, numbers, and dashes, 1–16 characters. Write the recovery phrase on paper before continuing, and never share it.

2. Fund the custody address. Send a small amount of OP ETH (roughly $1) to the custody address shown above. This comfortably covers registration, storage, and the setup steps; whatever is left over stays in the account. The recovery address never needs funding unless you actually use it to recover.

3. Register your FID. This sends a real transaction and assigns your permanent identity number.

$ lectern register theo
  custody  0x7f4d…
  …
  FID      3325849

To preview without sending, add --dry-run.

4. Rent storage. Farcaster requires a small storage allotment before your account can post — without it, casts silently fail. This sends a real transaction (preview with --dry-run).

$ lectern rent-storage theo --send

5. Claim your fname. This reserves your public @username with Farcaster's name service. There is no network fee.

$ lectern claim-fname theo
claimed: HTTP 200

By default the fname matches your account name. Use --fname <other-name> to claim a different handle.

6. Register a signer. This adds the per-app signing key that lets Lectern post on your behalf.

$ lectern register-signer theo
  signer registered. private key in Keychain (signer.theo).

Once this confirms, you can post, react, and follow.

7. Set your profile (optional). Remember that profile set only previews unless you add --send.

$ lectern profile set theo \
    --display "Theo" \
    --bio "Hello" \
    --pfp "https://example.com/me.png" \
    --send

You can also do this in the app under Settings → Profile.

8. Send your first cast. A cast is a Farcaster post. Again, cast only previews unless you add --send.

$ lectern cast theo "first cast" --send
posted to 1 hub:
  ✓ hub-api.neynar.com — HTTP 200

In the app, click the Compose button (the pencil icon) in the feed header instead.

flowchart TD
    A["lectern keys new — generate keys + recovery phrase"] --> B[Fund custody with ~$1 OP ETH on Optimism]
    B --> C["lectern register — get your FID (sends by default)"]
    C --> S["lectern rent-storage --send — required before posting"]
    S --> D["lectern claim-fname — reserve your @username"]
    D --> E["lectern register-signer — enable posting (sends by default)"]
    E --> F["lectern profile set --send — optional profile"]
    F --> G["lectern cast --send — your first post"]

A full end-to-end test of brand-new account creation is a pending quality-assurance item. Treat the funding guidance above as a comfortable safe margin rather than an exact fee schedule; real costs are small and any surplus remains in your account.

The macOS app at a glance

Lectern is a sovereign Farcaster client — it runs entirely on your Mac, holds your keys locally, and talks to the network directly rather than through a company's servers. This section explains what you see when the app opens and how the main controls fit together.

A note on two recurring terms. Your signer is the cryptographic key, stored in your Mac's Keychain, that authorizes you to post and react on Farcaster; nothing is published without it. The Hub is a public Farcaster server that stores and serves posts; Lectern reads from and writes to it on your behalf.

Layout 🖥️ App

The window is divided into three vertical panes that always stay in view, so you never lose your place when you open a post or switch accounts.

  • Sidebar (left) — your accounts and everything you can launch: tools, the $KEYNOTE Console, your followed channels, and your saved mini apps.
  • Feed (center) — your posts, channel posts, and the search palette.
  • Detail (right) — the post you selected and the conversation beneath it.

Larger windows, such as Settings, Direct Casts, and Snap Maker, open as a single panel that takes over the whole window rather than stacking on top of the three panes. This keeps the screen calm and predictable.

┌──────────────┬───────────────────────────┬──────────────────┐
│  SIDEBAR     │  FEED                     │  DETAIL          │
│              │                           │                  │
│  ACCOUNTS    │  @dwr  ·  2h              │  Selected post   │
│  TOOLS       │  Building something new…  │  ─────────────   │
│  $KEYNOTE    │  ───────────────────────  │  ↳ reply         │
│  CHANNELS    │  @v  ·  4h                │  ↳ reply         │
│  MINI APPS   │  gm, Farcaster            │  ↳   ↳ reply     │
│              │  ───────────────────────  │                  │
│              │  [ search palette · ⌘K ]  │                  │
└──────────────┴───────────────────────────┴──────────────────┘

The sidebar is organized into five labeled sections, top to bottom. Each one can be collapsed by clicking its header.

  • ACCOUNTS — the Lectern accounts stored on this Mac. The one currently in use is highlighted; click any other account to switch to it. The free tier supports up to 2 active accounts at a time.
  • TOOLS — the full toolkit, covered in Tools. Spaces (live audio rooms) appears here as a free entry.
  • $KEYNOTE — a single row that opens the KEYNOTE Console. It pulses amber when a new $KEYNOTE event arrives, such as someone boosting one of your posts.
  • CHANNELS — a channel is a topic-based area of Farcaster, similar to a forum board. This section lists channels you have followed locally and appears only once you have followed at least one. Right-click a channel to unfollow it. Rows pulse amber when new posts arrive.
  • MINI APPS — a mini app is a small web app that runs inside Lectern. This section lists the ones you have added. Click the grid icon in the section header to discover mini apps from your feed or to paste a URL.

About the pulse. The amber pulse is Lectern's quiet signal that something new has arrived. Each pulse runs for about three seconds as a soft breathing glow, then settles. To avoid distraction, a busy row will not pulse again for 45 seconds, though its unread count still rises. Three places use it today: the $KEYNOTE row for incoming boosts, individual channel rows for new posts, and the Spaces row for newly live audio rooms. You can turn off the Spaces pulse in Settings → Spaces.

flowchart TD
    A[Sidebar] --> B[ACCOUNTS · switch between your accounts]
    A --> C[TOOLS · Spaces and the full toolkit]
    A --> D["$KEYNOTE · opens the KEYNOTE Console"]
    A --> E[CHANNELS · followed channels, right-click to unfollow]
    A --> F[MINI APPS · grid icon to add or discover]
    D -.->|amber pulse on a new boost| G[Something new has arrived]
    E -.->|amber pulse on new posts| G
    C -.->|amber pulse on a newly live room| G

Composer 🖥️ App

The composer is where you write and send a post. Click the Compose button (the pencil icon) in the feed header to open it.

  • Channel picker — choose where the post goes, above the text editor.
  • Reply / quote — a reply automatically inherits its parent's channel; a quote embeds the original post.
  • Embedsdrag images in, paste a URL, or use the toolbar buttons for an emoji, a meme or GIF, an image, a cashtag chip, or a snap template. (Mentions are added inline by typing @ and picking from autocomplete.)
  • Toolbar — when the composer is narrower than about 540 pixels, the toolbar collapses to icons only and the snap templates fold into a single dropdown, so the byte counter is never crowded.
  • Drafts — your work auto-saves every two seconds, with a final save when you click close, so an embed added moments before closing is never lost.
  • SendTouch ID confirms, and the post is broadcast to the network.
┌─ Compose ──────────────────────────────────┐
│  [ #channel ▾ ]                            │
│  ┌──────────────────────────────────────┐  │
│  │ Write your post…                     │  │
│  │                                      │  │
│  └──────────────────────────────────────┘  │
│  [ $cashtag ] [ meme ] [ snap ▾ ] [ img ]  │
│  draft saved ·            234/320   [ Send ]│
└────────────────────────────────────────────┘

Settings (⌘,) 🖥️ App

Settings opens with ⌘, and presents sixteen sections in a left-hand rail. The most frequently used are summarized below.

  • profile — display name, bio, avatar (animated GIFs supported), banner, and links. Avatar and banner uploads save to the Hub immediately; text fields are saved together when you click save.
  • wallets — your verified Ethereum and Solana addresses, with the option to pick a primary.
  • direct casts — your private-message availability on Farcaster and XMTP, plus the optional Local Archive that preserves your inbox beyond farcaster.xyz's 7-day server-side deletion window.
  • notifications — turn macOS system notifications on or off; shows the current macOS permission state.
  • license — your Lectern Pro status shown as a colored pill (PAID / TRIAL / GIFTED / VERIFYING / LAPSED), a one-time Start 7-day trial button, and the KEYNOTE staking-discount panel.
  • appearancesystem, light, or dark mode, plus theme presets. The custom theme editor is a Pro feature.
  • network — a display-only audit of every network endpoint Lectern contacts, listed per chain.
  • developer — the Mini App tester (paste any URL, including a local http://localhost:3000, to launch it) and the Background Scheduler install button, which lets scheduled posts send while Lectern is closed.

The remaining sections — muted, blocked, scheduling, archive, export, spaces, ticker, and feedback — are covered where each feature is discussed elsewhere in this guide.

flowchart LR
    A[Press ⌘,] --> B[Settings opens]
    B --> C[profile · name, bio, avatar, banner]
    B --> D[wallets · verified addresses]
    B --> E[direct casts · private messages]
    B --> F[license · Lectern Pro status]
    B --> G[appearance · light, dark, themes]
    B --> H[developer · mini-app tester, scheduler]

Notifications 🖥️ App + ⌨️ CLI

Lectern delivers notifications in two places, and posts from accounts you have blocked are filtered out of both.

  • In-app inbox — the bell icon in the feed header, also reachable with ⌘⇧N. It collects mentions, replies, follows, likes, and recasts. Click an item to jump straight to it.
  • macOS system notifications — checked every five seconds, with end-to-end delay comfortably under ten seconds. Click one to open the relevant post inside Lectern.
┌─ Notifications ──────────── ⌘⇧N ─┐
│  @v liked your post              │
│  @dwr replied to you             │
│  @alice followed you             │
│  @bob recast your post           │
└──────────────────────────────────┘

Cold-start warmup modal 🖥️ App

The first launch — and any cold start afterward — briefly shows a one-time Warmup panel over a dimmed background. It loads each account's read-only state in parallel before you begin clicking, warming five cells per account: profile, pfp (profile picture), notifs (notifications), dms (direct messages), and feed.

The purpose is to prevent the brief confusion that can otherwise occur when you switch to a second or third account immediately after launch and see a stale profile or an empty feed for a few seconds.

  • Per-account retry — if a cell fails because of a momentary network hiccup, click retry to re-run just that cell.
  • Skip closes the panel without stopping the work. The warmup continues in the background, so your caches are warm by the time you reach each account.
  • Direct-message cells for inactive accounts display skipped (active-only) rather than ok, because loading messages requires that account's signer to be unlocked — and unlocking each one would prompt for biometric approval per account. The active account's message cell warms normally, since its signer is already unlocked.

The panel dismisses itself once every account has finished.

flowchart TD
    A[App launches cold] --> B[Warmup panel appears]
    B --> C[Each account warms 5 cells in parallel]
    C --> D[profile · pfp · notifs · dms · feed]
    D --> E{Cell result}
    E -->|ok| F[Cache is warm]
    E -->|failed| G[Tap retry · re-runs that cell only]
    E -->|inactive account's dms| H["skipped active-only · signer locked"]
    F --> I[All accounts done · panel dismisses]
    H --> I
    B -.->|Skip| J[Panel closes · warming continues in background]

Direct Casts modal (⌘⇧M) 🖥️ App

A direct cast is a private message. This modal, opened with ⌘⇧M, has two tabs.

  • Farcaster — available on accounts with a managed signer connected to farcaster.xyz. Self-custodied accounts see an initialize prompt until they connect.
  • XMTP — available on any account with a verified Ethereum address. New messages stream in live.

Unread counts from both tabs combine into a single badge on the message-bubble icon in the feed header.

┌─ Direct Casts ──────────── ⌘⇧M ──┐
│  [ Farcaster ] [ XMTP ]          │
│  ────────────────────────────    │
│  @alice    gm! ready to ship?    │
│  @bob      sent you the file     │
│  @carol    thanks 🙏             │
└──────────────────────────────────┘

Keyboard shortcuts 🖥️ App

Lectern's main actions all have keyboard shortcuts. The table below lists each one and what it does.

Shortcut Action
⌘K Search palette (fname, FID, channels, post text)
⌘/ Send feedback
⌘, Settings
⌘⇧N Notifications inbox
⌘⇧M Direct Casts
⌘⇧B Bookmarks
⌘⇧S Snap Maker
⌘⌥T Test Mini App… (opens Settings → Developer)
⌘⌥H Home feed
⌘⌥F For You feed
⌘⌥O Own Casts feed
⌘1..⌘9 Switch active account by slot
⌘⇧1..⌘⇧9 Launch saved mini app by slot
⌘⌃0 / ⌘⌃1 / ⌘⌃2 Appearance: System / Light / Dark
  ⌘K  search          ⌘⇧N  notifications
  ⌘,  settings        ⌘⇧M  direct casts
  ⌘/  feedback        ⌘⇧B  bookmarks
  ⌘⌥H home feed       ⌘⇧S  snap maker
  ⌘⌥F for you         ⌘1–9 switch account
  ⌘⌥O own casts       ⌘⇧1–9 launch mini app

Reading

Reading in Lectern always begins with a target: the account or post you want to look at. A target can be an FID (a permanent identity number, such as 3), an fname (a human-readable username, such as dwr or @dwr), or the name you gave a local account on this Mac. Wherever a command or field asks for a target, any of these will work.

See someone's profile

🖥️ Apptype their fname or FID into the search palette. A bare number such as 6131 resolves directly to that account. You can also click any avatar to open a profile, or use the account inspector for a fuller view.

The search palette opens with ⌘K and resolves four kinds of input: an fname, an FID, a channel, and post text (it searches the words inside posts). Typing / narrows the results to channels only.

┌─ Search ─────────────────────── ⌘K ─┐
│ dwr                                  │
│ ────────────────────────────────────│
│  PROFILES                            │
│   @dwr · Dan Romero · FID 3          │
│  CHANNELS                            │
│   /design                            │
│  POSTS                               │
│   "…what dwr said about clients…"    │
└──────────────────────────────────────┘

⌨️ CLI

$ lectern profile show dwr
$ lectern profile show dwr --json | jq '.profile.bio'
flowchart LR
    A[Press ⌘K] --> B[Type your target]
    B --> C{What did you type?}
    C -->|fname e.g. dwr| D[Opens that profile]
    C -->|FID e.g. 6131| D
    C -->|channel e.g. /design| E[Opens that channel]
    C -->|words| F[Finds posts containing them]

Account Radar — assessing whether an account is trustworthy 🖥️ App

Farcaster is open, so anyone can reply to anyone. Account Radar helps you judge, at a glance, whether an unfamiliar account is genuine, surfacing the warning signs that scam and impersonation accounts tend to show — directly on the post.

Every account has a permanent identity number, its FID (Farcaster ID). Because these numbers are assigned in order, a very high FID indicates a recently created account. Account Radar combines this with the content of the post and a few public profile facts to produce a single trust assessment, computed entirely on your Mac with no effect on how quickly your feed loads.

The shield indicator. When an account shows warning signs, a small shield appears in the top-left corner of its post:

  • Amber shieldcaution. One notable signal, such as a very new account or promotional "claim your airdrop" language.
  • Red shieldalert. Several signals together, or an account a large number of people have blocked.

Roughly 95% of accounts display no shield. This is intentional: the indicator appears only when there is something worth your attention.

Example. A reply arrives from @eth-rewards-now, marked with a red shield. Opening it shows the reasons — registered within the last two weeks, uses airdrop-promotion language, blocked by 312 accounts. That is enough to recognize a likely scam and disregard the post.

Vetting any account on demand. You are not limited to flagged accounts. Right-click any post and choose Vet @name to open the same assessment. For a more thorough check, select Deep check, which performs a single lookup and reports whether the account has a verified wallet address, the date of its first post, and the exact number of accounts that block it.

flowchart TD
    A[A post appears in your feed] --> B{Does the account show warning signs?}
    B -->|No — about 95% of accounts| C[No shield · feed stays clean]
    B -->|One notable signal| D[Amber shield · caution]
    B -->|Several signals, or widely blocked| E[Red shield · alert]
    D --> F[Open the shield, or right-click → Vet @name]
    E --> F
    C -.->|Any account can be checked manually| F
    F --> G[Assessment shown in plain language]
    G --> H[Optional Deep check · verified wallet, first-post date, block count]

See their recent posts

⌨️ CLI only. The app shows posts in your feed, but it does not have a dedicated "show me only this person's posts" view. The CLI does, with the casts command.

$ lectern casts dwr --limit 5
$ lectern casts dwr --limit 50 --json
$ lectern casts dwr --limit 5
@dwr · 2h   Building something new…
@dwr · 9h   gm
@dwr · 1d   Replying to @v: agreed
@dwr · 2d   Shipping this week.
@dwr · 3d   /design is underrated

Read a thread

A thread is a post together with all the replies beneath it.

🖥️ Appclick any post to open its thread in the Detail pane.

⌨️ CLI — pass the post's identifier and, optionally, how many reply levels to show with --depth.

$ lectern thread "1:0x8b54…" --depth 3
flowchart TD
    A[Pick a post] --> B{App or CLI?}
    B -->|App| C[Click it · thread fills the Detail pane]
    B -->|CLI| D[lectern thread with the post id and --depth 3]
    C --> E[Original post, then nested replies]
    D --> E

Your aggregated feed

Your feed is the merged stream of recent posts from the accounts you follow.

🖥️ App — this is the default view in the center pane. You can switch between feed modes — home, for you, own casts, curated, and view-as — and the keyboard shortcuts ⌘⌥H, ⌘⌥F, and ⌘⌥O jump to the first three.

⌨️ CLI

$ lectern feed myaccount --limit 30
$ lectern feed myaccount --limit 30
@v     · 4h   gm, Farcaster
@dwr   · 2h   Building something new…
@alice · 1h   Anyone at the meetup?
@bob   · 12m  Just shipped a fix.

Inspect on-chain state

These commands read public blockchain records for an account: its signing keys, its verified wallet addresses, and its ownership details.

⌨️ CLI only

$ lectern signers dwr               # current Ed25519 signers
$ lectern verifications dwr         # verified addresses
$ lectern fid 3                     # custody + recovery for an FID
$ lectern identify --address 0x…    # match a key to its FID
flowchart LR
    A[Pick a target] --> B[signers · its signing keys]
    A --> C[verifications · its verified wallets]
    A --> D[fid · custody + recovery owner]
    A --> E[identify · which FID owns an address]

One-screen overview

⌨️ CLI only. The status command prints a single summary of an account: its FID, fname, custody and recovery wallet balances, signer count, storage, and follow statistics.

$ lectern status myaccount
$ lectern status myaccount
FID         3
fname       dwr
custody     0x6b9b…   1.42 ETH
recovery    0x91a2…   0.00 ETH
signers     4
storage     3 units
follows     1.2k following · 480k followers

Writing

Lectern offers two ways to publish. The 🖥️ App gives you a visual composer and one-click buttons. The ⌨️ CLI (the lectern command you type in Terminal) gives you the same actions as text commands, which is useful for automation.

A cast is a Farcaster post — the network's equivalent of a tweet. Everything in this section is a kind of write: you are signing a message and broadcasting it to Farcaster's servers.

🖥️ Appclick the Compose button (the pencil icon) in the feed header to open the composer.

⌨️ CLI — every write defaults to a dry-run: Lectern builds and signs the message but does not broadcast it, so you can preview exactly what would be sent. Add the --send flag to actually publish. (Two on-chain commands — revoke-signer and register-signer — invert this; they send by default and take --dry-run to preview instead. Those are covered under Profile, identity, verifications.)

flowchart LR
    A[You write a cast] --> B{Which surface?}
    B -->|App| C[Compose button]
    B -->|CLI| D[lectern cast …]
    C --> E[Click Send]
    D --> F{--send flag?}
    F -->|No| G[Dry-run preview only]
    F -->|Yes| E
    E --> H[Broadcast to Farcaster]

Cast — publishing a post ⌨️ CLI

The cast command takes your account name and the text, then optional flags for replies, channels, and embeds. An embed is an attachment shown beneath a cast — a link, an image, or a quote-cast (another cast you are quoting).

$ lectern cast myaccount "hello" --send

# Reply to another cast (target written as fid:0xhash)
$ lectern cast myaccount "thanks" --reply "1:0x8b54…" --send

# Post inside a channel
$ lectern cast myaccount "shipping" --channel shyguy --send

# Attach embeds (plain URLs and quote-casts are auto-detected)
$ lectern cast myaccount "see this" \
    --embed "https://devflair.xyz/p/myaccount/card" \
    --embed "1:0x8b54…" \
    --send

Length limits are measured in UTF-8 bytes, so emoji and accented characters are counted correctly:

  • 0–320 bytes — a standard cast.
  • 321–1,024 bytes — a long cast, free for every account since Farcaster's 2025-06-16 Pro launch.
  • 1,025–10,000 bytes — a 10K cast, which requires Farcaster Pro. (This is the on-protocol Pro tier, separate from Lectern Pro.)
  • Past your tier's ceiling, the app composer splits a long post into a thread automatically. On the CLI, the bare lectern cast instead rejects over-length text and points you to lectern thread-cast (which builds the reply chain from a file).

Add a meme to a cast 🖥️ App only

Lectern bundles a composer for the popular Memegen.link meme service so you can build a reaction meme without leaving the app. Open it from the composer toolbar: the meme/gif button (the picture icon).

┌─ Composer toolbar ────────────────────────────────┐
│  [ 😀 emoji ]  [ 🖼 meme/gif ]  [ 📎 image ]  …    │
└───────────────────────────────────────────────────┘
        click "meme/gif"  →  meme builder opens

The workflow:

  1. The template rail loads on first open (one fetch from api.memegen.link/templates, cached for the session).
  2. Filter with the search box, or flip animated GIFs only to show GIF-capable templates.
  3. Pick a template. Lectern sizes the caption boxes to that template's actual layout — 1, 2, 3, or 4 lines depending on the meme. Each box's placeholder previews the kind of text it expects.
  4. Optional: click populate from example to drop the template's canonical captions into the boxes as a starting point.
  5. Optional: switch the format between PNG (static) and GIF (animated). Animated templates default to GIF.
  6. The preview renders live as you type.
  7. Click attach to cast to add the finished meme to your draft.

The meme becomes a standard image embed. Memegen encodes the captions directly into the image URL, so the meme still renders in any Farcaster client that loads the embed.

Delete a cast — uncast ⌨️ CLI

To remove one of your own casts, run uncast with the cast's id (fid:0xhash). You can only delete casts you authored. In the 🖥️ App, use the trash control or right-click a cast and choose delete.

$ lectern uncast myaccount "3325849:0xabc…" --send
flowchart TD
    A[Your cast] --> B{Are you the author?}
    B -->|No| C[Deletion refused]
    B -->|Yes| D[CastRemove built]
    D --> E{--send?}
    E -->|No| F[Dry-run preview]
    E -->|Yes| G[Cast removed from Farcaster]

Follow and unfollow

To follow someone is to subscribe to their casts; unfollow reverses it.

🖥️ AppFollow buttons appear in the profile popover and on feed rows.

⌨️ CLI — target by FID number or by fname (a Farcaster username such as @dwr):

$ lectern follow myaccount 6131 --send
$ lectern follow myaccount @dwr --send
$ lectern follow myaccount 6131 --remove --send

Follow is idempotent — running it again on someone you already follow is a harmless no-op, and the CLI reports (noop) rather than sending a duplicate.

React — like and recast

A reaction is a lightweight response to a cast. Lectern supports two types: a like (the heart) and a recast (sharing a cast to your own followers, like a repost).

🖥️ App — the heart (like) and recast icons sit in each cast's row. They light up when active; click again to undo.

⌨️ CLI

$ lectern react myaccount --cast "1:0xabc…" --type like --send
$ lectern react myaccount --cast "1:0xabc…" --type recast --send
$ lectern react myaccount --cast "1:0xabc…" --type like --remove --send

Block — two different mechanisms

A block stops an account from reaching you. Lectern offers two distinct block mechanisms, and they behave differently — read carefully, because choosing the wrong one will not do what you expect.

flowchart TD
    A[You want to block @scammer] --> B{Which mechanism?}
    B -->|macOS app| C[Settings → Blocked]
    B -->|CLI| D[lectern block … --send]
    C --> E[Local-only · stays on your Mac]
    E --> F[Hidden across feed, threads, notifications, DC inboxes, airdrop lists]
    D --> G[Published to Farcaster as a block record]
    G --> H[Block-aware clients may honor it]

In the macOS app — local-only. 🖥️ Open Settings → Blocked. Blocks made here are local-only: they live only on your Mac and are never sent to Farcaster. A blocked account is hidden everywhere on this machine — your feed, threads, notifications, direct-message inboxes, and airdrop recipient lists. Farcaster has no built-in block primitive of this kind, so nothing about an app-side block leaves your computer.

The Sync from Warpcast button (also under Settings → Blocked) performs a one-way, read-only import of the block list you already built in the legacy Warpcast app. Lectern fetches your existing blocks, shows you how many you already have locally and how many are new, and lets you choose whether to import them. Nothing is ever pushed back to Warpcast or to the network.

┌─ Settings → Blocked ──────────────────────────────────┐
│  [ ⤓ Sync from Warpcast ]                             │
│  Warpcast: 12 blocks · 9 already here · 3 new         │
│  [ Import 3 into local blocks ]                        │
│                                                        │
│  FIDS (9)                                              │
│  @noise-bot          · FID 884412      [ unblock ]    │
│  @airdrop-spam       · FID 901233      [ unblock ]    │
└────────────────────────────────────────────────────────┘

In the CLI — published to the network. ⌨️ The lectern block command is different: it publishes a block record to Farcaster (a "block"-typed link, using the same machinery as a follow). Block-aware clients can read and honor it; not every client respects them. As with other writes, --send is required to actually publish — the default is a dry-run preview — and --remove reverses the block.

$ lectern block myaccount 999 --send            # publish the block
$ lectern block myaccount 999 --remove --send   # reverse it

Do not assume the two are interchangeable. The app's block protects only your own Mac and stays private; the CLI's block is a public, on-network record other apps may act on.

Mute — a quiet, local filter

A mute is a personal, local filter. Muted accounts are hidden from your view, but — unlike a CLI block — a mute is never broadcast and no one is notified.

🖥️ App — manage your muted list under Settings → Muted.

⌨️ CLI

$ lectern mute myaccount 6131
$ lectern mute myaccount 6131 --remove
$ lectern mute myaccount --list

Because mutes never touch the network, the mute command applies the change locally and needs no --send flag.

Profile, identity, verifications

Your profile is the public face of your account: display name, bio, avatar, and links. Your identity is the cryptographic machinery underneath it — the keys that prove the account is yours. This section covers both, from everyday edits to recovering a compromised account.

Three terms recur below:

  • Signer — a delegated key Lectern uses to post on your behalf, so your most sensitive key never has to be touched for routine casts.
  • Custody key — the master key that owns your account (your FID, the permanent Farcaster ID number).
  • Verification — a signed, public statement that an external wallet (an Ethereum or Solana address) belongs to your account.
flowchart TD
    A[Custody key owns the account] --> B[FID · your identity number]
    B --> C[Signer posts on your behalf]
    B --> D[Verifications · linked external wallets]
    B --> E[Profile fields · name, bio, avatar, links]

🖥️ AppSettings → Profile. Text fields (display name, bio, links, location) batch together and commit when you click Save, which submits only the fields you changed. Avatar (pfp) and banner uploads behave differently: they persist immediately the moment the upload finishes, so you do not need to press Save for an image to take effect.

⌨️ CLI — each flag is one field; anything you omit is left untouched:

$ lectern profile set myaccount \
    --display "Maria Alvarez" \
    --bio "Building in public." \
    --pfp "https://example.com/maria.png" \
    --banner "https://example.com/banner.png" \
    --location "Lisbon" \
    --send
flowchart LR
    subgraph App["🖥️ Settings → Profile"]
        T[Text fields] -->|click Save| W1[Written together]
        I[Avatar / banner] -->|on upload| W2[Written immediately]
    end

A note on banners. A profile banner is a Farcaster Pro feature at the network level. If your account is not Farcaster Pro, Lectern falls back to its own off-protocol banner, which is displayed inside Lectern's macOS app only and is not visible in other clients.

Verify an external address ⌨️ CLI only

A verification publicly links an outside wallet to your account so others can trust that the address is really yours. To prove ownership, Lectern signs a short claim with that wallet's private key. The signing uses EIP-712 — an Ethereum standard for signing structured, human-readable data rather than a meaningless blob.

sequenceDiagram
    participant You
    participant Lectern
    participant Wallet as External wallet key
    participant FC as Farcaster
    You->>Lectern: lectern verify-address …
    Lectern->>You: prompt for the wallet's private key (hidden)
    You->>Lectern: enter key
    Lectern->>Wallet: sign EIP-712 ownership claim
    Wallet-->>Lectern: signature
    Lectern->>FC: publish verification (with --send)
    Note over Lectern: key is used once to sign, then discarded

Verify an Ethereum address. The private key is entered at a hidden (echo-off) prompt:

$ lectern verify-address myaccount --send
# (hidden prompt for the private key of the address being verified)

Verify a Solana address (accepts a base58 or hex key):

$ lectern verify-solana myaccount --privkey <base58-or-hex> --send

Remove a verification. Lectern auto-detects Ethereum vs. Solana from the address shape; force it with --protocol if needed:

$ lectern verify-remove myaccount --address 0x… --send
$ lectern verify-remove myaccount --address <base58> --protocol solana --send

Lectern uses the external private key only to sign the claim and then discards it. It permanently stores keys only for the accounts you manage — never for an address you are merely verifying (pass --dont-store on verify-address to be explicit).

Rotate your signer ⌨️ CLI only

Rotating a signer means retiring the key that posts on your behalf and issuing a fresh one — a sensible periodic hygiene step, and essential if you suspect a signer leaked. These two on-chain commands are send-by-default: they broadcast immediately and take --dry-run to preview instead (the inverse of the writing commands).

$ lectern signers myaccount                       # list active signers
$ lectern register-signer myaccount               # add a new signer
$ lectern revoke-signer myaccount                 # revoke the current signer
$ lectern revoke-signer myaccount --dry-run       # preview without sending

Add --clear-keychain to the revoke to also delete the local private key once revocation succeeds.

flowchart LR
    A[register-signer] --> B[New signer added on-chain]
    C[revoke-signer] --> D[Old signer removed on-chain]
    D -.->|--clear-keychain| E[Local key also deleted]

Caution. Revoking your active signer means the account cannot cast, react, or update its profile until you run register-signer again. The command warns you before it proceeds.

Recover a compromised custody ⌨️ CLI only

If your custody key (the master key that owns your account) is compromised, recovery moves your FID to a fresh custody address using your separate recovery key. The default mode generates a brand-new custody key in memory, signs the on-chain transfer, and — only after the transaction succeeds — writes the new key into your Keychain and purges the old one.

$ lectern recover myaccount --send
sequenceDiagram
    participant You
    participant Lectern
    participant Chain as Farcaster IdRegistry
    participant KC as Keychain
    You->>Lectern: lectern recover myaccount --send
    Lectern->>Lectern: check on-chain recovery address matches your recovery key
    alt mismatch
        Lectern-->>You: refuse with a clear error
    else match
        Lectern->>Lectern: generate fresh custody key in memory
        Lectern->>Chain: sign + broadcast custody transfer
        Chain-->>Lectern: success
        Lectern->>KC: swap in new custody, purge old
    end

Pre-condition. The recovery address recorded on-chain must match the recovery key in your Keychain. Lectern checks this first and refuses with a clear error — rather than wasting gas on a transaction guaranteed to fail — if they do not match.

There is also an external mode (--to-address 0xNEW) for migrating custody into a wallet you control elsewhere, such as a mnemonic-backed Rabby or MetaMask account. In that mode Lectern prints the EIP-712 transfer data for your external wallet to sign, then completes the move when you re-run with the resulting --transfer-sig.

After any recovery, your old signer is still registered on-chain. You will typically want to revoke it and register a new one:

$ lectern revoke-signer myaccount
$ lectern register-signer myaccount

Direct casts and XMTP

🖥️ App only.

Lectern brings private messaging into the same window as your feed. Open it with ⌘⇧M, or click the message bubble icon in the feed header. A single modal hosts a conversation list on the left and the open thread on the right.

Two separate messaging systems live side by side here, and it helps to understand the difference before you start.

  • Farcaster Direct Casts are the private messages you would see in the official farcaster.xyz app. They work only for accounts whose signing key was registered through farcaster.xyz. If you created a fully self-custodied account in Lectern, this rail shows "tap initialize" until you connect one.
  • XMTP (it stands for Extensible Message Transport Protocol) is an open, independent messaging network — think of it as email built for crypto wallets rather than email addresses. It works for any account that has a verified Ethereum address, and new messages arrive in real time.

Lectern picks a sensible starting rail for you. If your account has no Farcaster messaging but does have XMTP, the modal opens straight to the XMTP rail so you are never staring at a dead screen.

flowchart TD
    A[Open Direct Casts ⌘⇧M] --> B{Which account?}
    B -->|Registered via farcaster.xyz| C[Farcaster rail · full DC history]
    B -->|Self-custodied, no Farcaster signer| D[Farcaster rail shows 'tap initialize']
    B -->|Has verified ETH address| E[XMTP rail · open messaging]

XMTP Group Chats — messaging more than one person 🖥️ App

Lectern reads and writes XMTP group conversations in both directions. There are three ways to start or grow a group.

Create a new group. In the Direct Casts toolbar, click + XMTP group. The composer accepts an optional group name, an optional first message, and a list of member Ethereum addresses (separate them with spaces, commas, or new lines). You are added as a member automatically.

Convert a one-to-one conversation. Open an existing direct message and click [add user] in the header. The composer pre-fills with the person you were already talking to; add more members and an optional name, and Lectern starts a fresh group that includes everyone.

Invite someone by cast or message. Open a group, then use the [invite link] button in the header. Lectern copies a link of the form https://lctrn.app/g/<id>?inviter=0x.... Paste it into any cast or message. The recipient sees a green-bordered card with a [request to join] button. Tapping it sends you an automatic message asking to be added; you then use [add user] to bring them in.

This handoff exists because XMTP groups have no "self-join" feature — by design, an existing member must add each new person. The Lectern invite card simply makes that request a single tap instead of a manual back-and-forth.

sequenceDiagram
    participant You
    participant Link as lctrn.app/g link
    participant Them as Recipient
    You->>Link: Copy [invite link]
    You->>Them: Paste link into a cast or DM
    Them->>Them: Sees green 'request to join' card
    Them->>You: Taps it — auto-DM requesting to join
    You->>Them: Use [add user] to add them

Local message archive — keeping your Direct Casts 🖥️ App

Farcaster's older Direct Cast service deletes the contents of messages after roughly seven days. The XMTP rail is unaffected — XMTP stores its own message history — but Farcaster DCs can vanish from under you.

To prevent that, open Settings → Direct Casts → Local Archive and turn on the Local Archive. Lectern then keeps a private, encrypted copy of your Farcaster DCs on your own Mac, so they survive the seven-day server-side deletion.

The archive is sealed with AES-GCM (a strong, standard encryption method) using a key derived from your account's custody key — the master key that proves you own the account. Two consequences follow: nobody without that key can read the archive, and if you ever lose custody of the account, the archive becomes unreadable too. Files live under ~/Library/Application Support/Lectern/dc-archive/<account>/.

Unread badge. The message-bubble icon shows one combined unread count across both rails. Click it to jump straight to the unread thread.

flowchart LR
    A[Farcaster DC arrives] --> B[Settings → Direct Casts · Local Archive ON]
    B --> C[Sealed with AES-GCM · key from custody key]
    C --> D[Stored on your Mac]
    E[Server deletes body after ~7 days] -.does not affect.-> D

Spaces

🖥️ App only. Find it in the sidebar under TOOLS → Spaces.

Spaces are Farcaster's live audio rooms — drop-in conversations you can listen to, react in, or host yourself, similar to a live radio show with a call-in line. Lectern handles discovery, native listening, emote reactions, and hosting. Because this uses an undocumented Farcaster interface, the feature can change without notice.

One-time setup — Connect 🖥️ App

The first time you open Spaces, click Connect. A sheet appears with farcaster.xyz loaded inside an embedded web view. Sign in the normal way — email or phone through Privy, the same sign-in service Farcaster's own website uses.

A session bearer (the temporary access pass a website issues your browser after you log in) is what Lectern needs. The moment farcaster.xyz makes its first signed-in request, Lectern captures that pass in-process and stores it in the macOS Keychain. It is never sent to any Lectern-operated server.

Your custody key stays out of this entirely: the sign-in signature happens inside Privy's own pop-up, exactly as it does anywhere else on the web.

sequenceDiagram
    participant You
    participant WebView as farcaster.xyz (in-app)
    participant Privy
    participant Keychain as macOS Keychain
    You->>WebView: Click Connect, sign in
    WebView->>Privy: Email / phone login
    Privy-->>WebView: Session bearer issued
    WebView->>Keychain: Lectern captures bearer, stores locally
    Note over Keychain: Never leaves your Mac

Discover — finding rooms 🖥️ App

Once connected, the modal shows two tabs.

  • LIVE — rooms happening right now. Each tile shows the topic, the host's profile picture, listener and speaker counts, and a "live for X minutes" duration. Click [listen] to open a join-confirmation step; audio begins immediately on confirm, with your microphone muted by default (headphones recommended).
  • SCHEDULED — upcoming rooms. Each tile shows the topic, host, and start time. Click [rsvp] to toggle your RSVP, which uses the same mechanism farcaster.xyz does.

You can also export scheduled rooms as a .ics calendar file using [export .ics] — drop it into Calendar.app and each event lands with one-click RSVP links.

┌─ SPACES ───────────────────────  [ + host ] [ close ] ┐
│  ( LIVE )  ( SCHEDULED )                              │
├───────────────────────────────────────────────────────┤
│  ◉  Base builders open mic        @dwr   live for 24m │
│     🎧 312 listeners · 🎙 5 speakers      [ listen ]   │
├───────────────────────────────────────────────────────┤
│  ◉  Friday founder AMA            @ace   live for 8m  │
│     🎧 47 listeners · 🎙 2 speakers       [ listen ]   │
└───────────────────────────────────────────────────────┘

In the room — listening and reacting 🖥️ App

Each speaker appears as a profile-picture circle; whoever is currently talking gets an amber ring. Along the bottom bar sit six emote reactions — ❤️ 💯 😂 🔥 👏 🫡 — in that order. Tap one to send it; the emote floats up the screen with a slight sideways drift so rapid taps fan out instead of stacking. Other listeners' reactions appear the same way.

For speakers, a prominent microphone toggle sits at the top — a large pill that turns red when you are live and stays surface-colored when muted. Your first unmute triggers the macOS microphone-permission prompt; if you decline by accident, grant it later under System Settings → Privacy & Security → Microphone. If another app (Zoom, Discord, FaceTime, OBS, or a browser tab already in a Space) is holding the microphone, Lectern automatically resets its audio engine and retries once, then shows an inline explanation if it still cannot start.

flowchart LR
    A[Tap emote] --> B[❤️ 💯 😂 🔥 👏 🫡]
    B --> C[Floats up with drift]
    D[Speaker taps mic] --> E{First time?}
    E -->|Yes| F[macOS mic prompt]
    E -->|Device busy| G[Auto-reset + retry once]
    G -->|Still fails| H[Inline error: quit Zoom/Discord/etc.]

Hosting — running your own room 🖥️ App

Click + host in the Spaces toolbar, name your room, and start. End the room with the end space button in the role banner. As host, right-click a participant tile for Promote @name to Speaker or Demote to Listener (these host-only controls appear on the stage tiles). Promoting grants permission to speak: if the person hasn't already raised their hand, it sends them a stage invite they accept on their end, and their microphone unlocks once that permission goes through — it is not an instant client-side flip. (Promoting a passive listener who is in another Farcaster client works the same way; they accept the invite there.)

flowchart TD
    A[+ host → name → start] --> B[You are host]
    B --> C[Right-click a participant tile]
    C --> D["Promote @name → grants speak permission / sends invite"]
    C --> E[Demote to Listener]
    B --> F[end space button]

Identity strip — who you hear vs. who you post as 🖥️ App

Above the in-room composer, a strip shows two roles. listening as is the account your farcaster.xyz session belongs to — the one actually hearing the room. casting as is the Lectern-managed account whose signer will sign any comment you post from the room. When the two differ, the casting label flashes yellow with a mismatch note ("your comment posts from a different account than the one hearing the room"); click it to switch accounts. Lectern matches them automatically whenever it can.

Cast about the Space 🖥️ App

The bottom of the room holds a full composer — emoji, image, GIF, and embeds, identical to the regular cast composer. Anything you post from here automatically embeds the current Space's URL.

Recording — capturing the audio 🖥️ App

Recording is experimental and opt-in under Settings → Spaces. When enabled, Lectern records the full mix it is subscribed to — the remote speakers you hear and your own microphone when you are speaking — and saves it to ~/Documents/Lectern/spaces-recordings/ as a .caf file. A .caf (Core Audio Format) file is Apple's lossless audio container; you can transcode it to MP3 or M4A with any audio tool afterward. Note that a client can only record what it is subscribed to (the host and speakers it hears, plus your own mic) — a guaranteed every-participant archive would require server-side recording, which Lectern deliberately does not run.

flowchart LR
    A[Settings → Spaces · Recording ON] --> B[Remote speakers + your own mic]
    B --> D[~/Documents/Lectern/spaces-recordings/*.caf]

Minimize — keep a Space open while you work 🖥️ App

While you are connected, a minimize button (the pip icon) in the Spaces toolbar collapses the room into a small draggable floating card pinned to the corner of the screen, while the Space keeps running — audio and your heartbeat continue uninterrupted. The card shows the host's avatar and the room topic, with controls to toggle your mic (speakers and hosts), expand back to the full room, and leave. This lets both hosts and listeners browse the feed, compose, or use any other part of Lectern without dropping the Space. Click the card to return to the full view.

Other surfaces 🖥️ App

  • Settings → Spaces — disconnect or reconnect, the sidebar-pulse toggle, and the experimental recording opt-in.
  • Settings → Ticker → Sources → Live Farcaster Spaces — rotate live rooms through the title-bar ticker alongside (or instead of) coin prices.
  • Sidebar pulse — when a new room goes live, the sidebar Spaces row pulses amber once. Toggle this under Settings → Spaces.
  • Embeds in casts — any cast that embeds a farcaster.xyz/~/spaces/... URL renders as a landscape card with the host's banner, listener count, and a native [listen] button that deep-links into Spaces.

Cashtags + token references

When you write $KEYNOTE in a cast, the dollar-sign styling is cosmetic only — Farcaster cannot tell which token you mean from the symbol alone. The real reference is a token embed: an attached URL of the form https://farcaster.xyz/~/c/<chain>:<address> (an embed is any link or media Farcaster attaches to a cast and renders as a card). A cashtag is the human-friendly $TICKER label that points at that embed.

Lectern's Cashtag directory solves the tedious part: it stores the (ticker, chain, contract address) mapping once and reuses it for every future cast, so you never paste a contract address twice.

Add a token to the directory 🖥️ App

In the composer toolbar, click [$] (the chart icon) to open the picker.

First time you reference a token:

  1. Click [+ add new].
  2. Enter the ticker (for example KEYNOTE), pick the chain (Base, Ethereum, Optimism, Arbitrum, Polygon, or Solana), and paste the contract address.
  3. Save. Lectern checks the address against DexScreener (a public service that lists actively traded tokens). If found, the token's name and symbol are cached automatically. If the token is too new or too thinly traded to be listed yet, it still saves — the picker simply marks it for re-checking later.

Every time after that: the picker lists your saved tokens. Pick one, and in a single tap $TICKER is appended to your draft and the token embed URL is attached.

flowchart TD
    A[Composer → $ button] --> B{Token saved already?}
    B -->|No| C[+ add new: ticker, chain, address]
    C --> D[Save → check DexScreener]
    D -->|Found| E[Name + symbol cached]
    D -->|Not listed| F[Saved anyway, marked for re-check]
    B -->|Yes| G[Pick from list]
    E --> H[$TICKER text + embed URL attached]
    F --> H
    G --> H

How cashtags render in casts 🖥️ App

When a cast embeds a token URL — yours or anyone else's — Lectern renders an inline price card showing the token's name, symbol, USD price, 24-hour change, market cap, and 24-hour volume. To keep figures fresh, Lectern queries data sources in priority order:

  1. GeckoTerminal (primary) — the fastest index for Base mid-caps and Lectern-economy tokens such as $KEYNOTE. Tap the card to open GeckoTerminal's chart.
  2. DexScreener (secondary) — broader coverage of the long tail when GeckoTerminal has not indexed a token yet.
  3. Block-explorer fallback (tertiary) — if both miss, the card shows a "not indexed by DexScreener" note with a Basescan, Etherscan, or Solscan link so you can still verify the contract yourself.
flowchart LR
    A[Cast embeds token URL] --> B[GeckoTerminal]
    B -->|Found| Z[Price card · price · 24h · mcap · vol]
    B -->|Miss| C[DexScreener]
    C -->|Found| Z
    C -->|Miss| D[Block-explorer card · Basescan / Etherscan / Solscan]

Where the data lives 🖥️ App

The directory is stored at ~/Library/Application Support/Lectern/cashtag-directory.json. It is shared across all your Lectern accounts on this Mac — a KEYNOTE → 0x… mapping is correct no matter which account you cast from. Entries that have not been re-checked in 24 hours show an "(unverified — older than 24h)" hint in the picker; re-saving an entry re-verifies it against DexScreener and clears the hint. The hint never blocks you from using a token — it is only a prompt to double-check a contract you have not touched in a while.

┌─ $ CASHTAG ─────────────────  [ + add new ] [ close ] ┐
│  filter by ticker:  ____________________              │
├───────────────────────────────────────────────────────┤
│  $KEYNOTE  · BASE                                      │
│  0x9d7f…a3c1                                  [ 🗑 ]   │
├───────────────────────────────────────────────────────┤
│  $DEGEN    · BASE   (unverified — older than 24h)     │
│  0x4ed4…b9f0                                  [ 🗑 ]   │
└───────────────────────────────────────────────────────┘

Mini apps and wallet

Farcaster lets independent developers ship small interactive web programs that run inside a feed — buy a token, mint a collectible, RSVP to an event, play a game — without leaving the conversation. The open standard calls these mini apps (the older term, frames, refers to the same idea: a self-contained interactive card embedded in a post). Lectern runs them in a sheet using the official @farcaster/miniapp-host SDK, so they behave exactly as their authors intended.

🖥️ App only. Mini apps need a live, sandboxed runtime; there is no command-line equivalent.

What a mini app can ask Lectern to do. A mini app never touches your keys directly. When it needs something sensitive, Lectern intercepts the request, shows you a plain-language confirmation, and requires Touch ID before anything happens. Lectern supports the wallet operations that do not depend on Farcaster's hosted servers:

  • Sign in with Farcaster (SIWF) — proves who you are to the mini app without a password. SIWF is the standard "log in with your Farcaster account" handshake. Touch ID gates it.
  • Personal sign and typed-data signing — the mini app asks you to sign a message (not a payment) to prove consent. You review and approve.
  • eth_sendTransaction — sends a transaction. Lectern opens an approval modal showing the recipient, value, a calldata summary (a readable description of what the transaction does), and a chain badge. Touch ID confirms; Lectern broadcasts on Base, Ethereum, Optimism, or Arbitrum.
  • wallet.sendToken — transfers a specific token. An ERC-20 is the common token standard on these networks (USDC, for example, is an ERC-20). Runs through the same approval flow.
  • wallet.swap — opens the wallet panel pre-filled with the requested trade; you confirm and broadcast.
  • view_token — opens the token's contract page in the correct block explorer.
  • send_token — pre-fills the wallet Send sheet with the requested recipient and amount.
sequenceDiagram
    participant U as You
    participant M as Mini app
    participant L as Lectern
    M->>L: Requests eth_sendTransaction
    L->>U: Shows recipient, value, calldata, chain
    U->>L: Touch ID
    L->>L: Signs with your key on-device
    L-->>M: Broadcasts and returns result

The wallet. The wallet that powers these requests lives in Settings → Wallets, and is also reachable directly from the sidebar (see Wallet under Tools). It has three tabs: Receive (a QR code plus click-to-copy address), Send (token picker, fname-to-address resolution, balance pill, percent strip), and Swap (single-chain routing plus cross-chain bridging).

Discover and add mini apps

🖥️ App — open the Apps entry in the sidebar, then search the directory or paste a mini app's URL to add and open it immediately. Recently used URLs persist across launches.

┌─ Apps ──────────────────────────────────────┐
│  Search:  [ pixel quest____________ ]  🔍    │
│  ──────────────────────────────────────────  │
│  Or paste a URL:                             │
│  [ https://example.app/miniapp ]  Add →      │
│  ──────────────────────────────────────────  │
│  Recently opened                             │
│   • Pixel Quest                              │
│   • Yield Tracker                            │
└──────────────────────────────────────────────┘

Mini app push notifications

Lectern does not surface mini-app push notifications, by design. If you added a mini app on farcaster.xyz or Warpcast, its notifications continue to arrive there. They will not also appear in Lectern.

This is a deliberate architectural decision, not a missing feature:

  • The Farcaster mini-app specification requires the receiving client to operate a public webhook endpoint that accepts arbitrary POSTs from any mini-app server. Notifications are pushed to that endpoint, and the host then dispatches them to its users.
  • Lectern is a Mac app. Its design intentionally keeps server surface area near zero. Operating a public notification ingest endpoint would invert that posture and take on open-ended moderation responsibility for whatever any mini app chose to send.
  • The specification offers no documented way for a third-party client to read notifications that another host received. We checked.

What this means in practice. For now, your primary Farcaster client (farcaster.xyz or Warpcast) is where mini-app notifications surface. Lectern remains the place to discover, launch, and use mini apps — it simply does not receive their push notifications. If Farcaster ships a sovereign-friendly read pattern, or a future Lectern release adds an optional integration through a notification provider, this section will be rewritten. Until then it is an honest gap.

Build your own

Open Settings → Developer to point Lectern at a local development server such as localhost:3000 and iterate inside the real runner. Recent development URLs persist across launches.

Tools

Lectern groups its power features under a TOOLS heading in the sidebar. Some are part of Lectern Pro, the paid tier; others are free for every account.

  • Pro: Snap Maker, Follow Manager, Cast Analytics, Airdrop
  • Free: Wallet, Bookmarks, Scheduled, Spaces

The $KEYNOTE Console sits in its own sidebar section, just below TOOLS.

Snap Maker (Pro) 🖥️

A snap is a small, self-contained interactive card you can attach to a post — a profile summary, a poll, a tip button, an event invite. Lectern's other readers render snaps natively in the feed; on other clients they appear as a rich link preview. Snap Maker lets you author one without writing any code.

Open it with ⌘⇧S. Pick one of 14 templates, fill in the fields, watch the live preview update as you type, then press Create + open composer to publish the snap and seed a fresh composer with its URL ready to post.

The templates are: Profile card, Linktree (a hub of your links), Announcement, Event invite, Tip jar, Quote, Poll, AMA, Claim (a single-button call to action), Verification badge (read-only proof of an address or membership), Mini app launcher, Tip (one-tap amount buttons), Leaderboard, and RSVP.

┌─ Snap Maker ───────────────  ⌘⇧S ─┐
│ TEMPLATES        │  Preview        │
│ ▸ Profile card   │ ┌─────────────┐ │
│   Linktree       │ │  @ada       │ │
│   Poll           │ │  Engineer.  │ │
│   Event invite   │ │  ◆ Base ✓   │ │
│   Tip jar        │ └─────────────┘ │
│   … (14 total)   │ [Create + open  │
│                  │   composer]     │
└───────────────────────────────────┘

⌨️ CLI: lectern snap card / poll / claim / verify / mini-app-launch <name> — see the Snaps section.

Wallet (free) 🖥️

The same wallet that powers mini-app transactions, surfaced as a standalone tool. It opens to Receive by default, because the most common first action is showing someone your address. Three tabs:

  • Receive — a 180×180 QR code of the selected address, plus click-to-copy.
  • Send — pick any token (the picker scans Blockscout, a public block-explorer service, for the ERC-20 tokens the sending wallet holds, and always offers native ETH), type a recipient address or an fname (Lectern resolves the name to that account's verified address), set an amount, and broadcast. A balance pill and a 25 / 50 / 75 / MAX percent strip make sizing the amount quick. Touch ID confirms before broadcast.
  • Swap — exchange one token for another. On a single network, Lectern routes the trade through 0x (a service that finds the best available exchange rate). Across two networks, it bridges native ETH through Across (a bridge moves assets from one blockchain to another). A live quote refreshes on a 15-second countdown so you always confirm against a current rate.
flowchart LR
    R[Receive · QR + copy] --- S[Send · token + recipient]
    S --- W[Swap · 0x route or Across bridge]
    S -.Touch ID.-> B[(Broadcast)]
    W -.15s quote.-> B

Follow Manager (Pro) 🖥️

Bulk maintenance for your follow list. Review non-mutuals (people you follow who do not follow you back, and the reverse), surface inactive accounts (no post in N days — the default is 90), and batch-unfollow in one action. This is Pro-gated because the inactivity check sends one request to the Farcaster network per account you follow, which is expensive at scale.

┌─ Follow Manager ───────────────────────────┐
│ [ Non-mutuals ]  [ Inactive ]   no cast in │
│                                  [ 90 ] days│
│  ☑ @stale_account     last cast 142d ago   │
│  ☑ @dormant_user      last cast  97d ago   │
│  ☐ @still_active      last cast   3d ago   │
│  ──────────────────────────────────────────│
│         [ Unfollow selected (2) ]          │
└────────────────────────────────────────────┘

⌨️ CLI counterparts:

$ lectern social stats myaccount
$ lectern social mutuals myaccount
$ lectern social one-way myaccount        # you follow, they don't follow back
$ lectern social orphans myaccount        # they follow, you don't follow back
$ lectern social follow-back myaccount --send
$ lectern social prune myaccount --send

Bookmarks (free) 🖥️ + ⌨️ CLI

Saved posts, available in both the app and the CLI. See Bookmarks below.

Scheduled (free) 🖥️

A single place to view and manage scheduled casts and queued engagements (a Like, Recast, or Quote-cast lined up from BoostModal). The sidebar row shows a badge with the number of pending items.

┌─ Scheduled ────────────────────────────────┐
│ Casts                                       │
│   • "Shipping notes…"    Mon 09:00          │
│ Queued engagements                          │
│   • Recast @founder       fires in ~2h      │
│   • Like   @designer      fires in ~40m     │
└────────────────────────────────────────────┘

⌨️ CLI: lectern engagement-queue ... manages queued engagements. (Scheduling new casts from the CLI is not yet available.)

Cast Analytics (Pro) 🖥️

Choose a scan cap — 50 / 100 / 200 / 500 / 1000 posts — and Lectern walks back through your casts to build a complete picture of your reach: an engagement timeline, a heatmap of when your audience is reading, a posting-cadence chart, a leaderboard of your top engagers, and an on-device growth plan.

Scans checkpoint every 25 casts, so you can cancel and reopen later without losing progress. The cache lives at ~/Library/Application Support/Lectern/analytics.<fid>.json.

flowchart TD
    A[Pick scan cap · 50–1000] --> B[Walk your casts · checkpoint every 25]
    B --> C[Timeline]
    B --> D[Heatmap]
    B --> E[Cadence]
    B --> F[Top-engager leaderboard]
    B --> G{Apple Silicon + macOS 26?}
    G -->|Yes| H[Apple Intelligence growth plan]
    G -->|No| I[Analytics only]

The growth plan runs entirely on your Mac using Apple Intelligence and requires Apple Silicon plus macOS 26. Intel Macs and earlier macOS versions get every analytics view except the AI plan.

Airdrop (Pro) 🖥️

An airdrop distributes a token to many recipients at once. Lectern sends through the canonical Disperse contract — a well-known, audited, no-fee program deployed identically on Base, Optimism, Arbitrum, and Ethereum that pushes one token out to a long list of addresses in a single transaction. The five-step wizard:

flowchart TD
    A[1 · From · wallet + chain] --> B[2 · Token · Blockscout discovery, paste contract, or native ETH]
    B --> C[3 · Cohort · Followers · Following · Boosters · Custom paste]
    C --> D[4 · Amount · per-recipient or split evenly]
    D --> E[5 · Review · fee quote shown]
    E -->|Touch ID| F[(Real broadcast via Disperse)]
    F -.> G{Over 500 recipients?}
    G -->|Yes| H[Auto-chunk, nonce-sequenced]
  1. From — pick the sending wallet and the network it sends on.
  2. Token — choose the token to send. Lectern discovers tokens you hold via Blockscout, or you can paste a contract address, or select native ETH.
  3. Cohort — choose who receives it. A cohort is simply the recipient group. Options are Followers (accounts that follow you), Following (accounts you follow — usually higher-signal, since outbound follows are deliberate), Boosters (addresses that boosted your cached casts with KEYNOTE), or Custom paste (a list of FIDs and/or addresses, mixed accepted). Lectern resolves each account to its first verified ETH address, falling back to the custody address.
  4. Amount — send the same amount to every recipient, or set a total and split it evenly across the cohort.
  5. Review — confirm the recipient count, per-recipient amount, and the Lectern fee before broadcasting.

This is a real broadcast, signed with the wallet you chose in step one and confirmed with Touch ID.

Fee. Airdrops to fewer than 10 recipients are free. At 10 or more, Lectern charges $1 USDC plus 25 basis points (0.25%) of the airdrop value, capped at $25. The fee is sent as a separate transfer immediately before the disperse, so it is always a distinct, visible transaction. If the token cannot be priced, the fee falls back to a flat $1.

Large lists. Past 500 recipients per transaction (a calldata-size limit), Lectern automatically chunks the airdrop into multiple transactions with explicit-nonce sequencing so they land in order.

$KEYNOTE Console 🖥️

A live, on-chain economic dashboard for $KEYNOTE, Lectern's token. Every figure is read directly from Base mainnet — nothing here is simulated. Two terms used throughout: to boost a post is to spend KEYNOTE to amplify it, and to stake is to lock KEYNOTE in a contract (the vault) in exchange for benefits. The console favors visualizations over a wall of numbers. From top to bottom:

  • Hero panel — your live KEYNOTE wallet balance, animated so it eases from the old value to the new one on each refresh. Beside it sits a compact contract-address chip (shortened address, copy button, open-on-Basescan button). Below are three metric cards: claimable Pro allocation, claimable boost earnings, and lifetime boosted.
  • Pool composition bar — a single stacked horizontal bar showing the relative sizes of the four global pools (Pro allocation, Staker yield, Currently staked, Vault waiting) with a percent-labeled legend.
  • Vault drip indicator — a pulsing drop glyph when active, or a locked padlock before the cliff, with the live vault claim status and a countdown to the next claim window.
  • Pro allocation list — each weekly allocation as a tap-to-claim row.
  • Boost earnings list — your unclaimed boost-engagement earnings, one row per epoch.
  • Stake tier gauge — a circular gauge showing your current tier with a "percent to next tier" indicator.
  • Economy runway bar — a Healthy / Watch / Strict / Critical strip with a marker on the current tier, plus burn-rate and months-remaining stat blocks.
  • Boost history — your past per-cast boost spending, most recent first.

The share button in the header copies a public, no-login link to your allocation page: https://lctrn.app/keynote/allocation?addr=<your>.

Boosting a cast. Every cast row carries a bolt icon. Pressing it opens BoostModal, where you set an amount and optionally turn on engage-on-boost — a Like, Recast, or Quote-cast sent automatically from your own Farcaster signer 5 minutes to 6 hours later (randomly jittered) so the boost itself, not the engagement, is the visible signal. Per-week caps apply.

sequenceDiagram
    participant U as You
    participant M as BoostModal
    participant C as Base mainnet
    participant S as EngagementScheduler
    U->>M: Tap bolt, set amount
    U->>M: Opt in to engage-on-boost
    U->>M: Touch ID
    M->>C: Boost transaction (custody key)
    M->>S: Queue Like / Recast / Quote
    S-->>C: Fires 5min–6hr later (jittered)

Claims and staking are real money. Claiming a Pro allocation or boost earnings, and staking or unstaking, are genuine Base mainnet transactions signed with your custody key. Each is gated by Touch ID before it broadcasts.

The staking tiers grant a discount on Pro: 25M KEYNOTE → 15%, 50M → 25%, 100M → 40%. For the full economics — token contracts, the weekly Pro allocation cadence, boost-engagement settlement, and the runway model — see lctrn.app/keynote.

Lectern Pro: trial and subscription

Lectern is free to use for everyday Farcaster. Lectern Pro is the paid tier that unlocks the power tools — and it is designed around a promise we never break: you are never locked out of your own client.

Free tier. Real-time feed, system notifications, XMTP direct casts, the full reading surface, profile editing, blocks, mutes, mini-app launching, and wallet send. Up to 2 active accounts.

Pro tier. Snap Maker, Cast Analytics, Airdrop, Follow Manager, theme customization, AI helpers (reply ideas, tone tweak, thread TL;DR — requires an Apple Silicon Mac), Cast Archive, unlimited accounts, and no cast stamp.

A full feature-by-feature comparison lives at lctrn.app/pricing. For $KEYNOTE staking discounts, see lctrn.app/keynote.

A note on two words used throughout this section. To stake means to lock a quantity of a token in a contract to signal commitment and, here, to earn a discount — you can withdraw it later. Your custody key is the private key that owns your Farcaster account and signs on-chain transactions; it lives in your Mac's Keychain and Lectern never transmits it.

Start the 7-day trial

The trial is a one-time, 7-day unlock of every Pro feature, free, with no payment method required. It can be started once per Mac.

🖥️ App — open Settings → License → Start 7-day trial. The button only appears if you have never used the trial.

⌨️ CLI

$ lectern license trial start

When the 7 days elapse, Pro features lock again unless you subscribe.

Subscribe — pay on-chain in-app

Pro costs 6.9 USDC per month or 79 USDC per year (a small saving over twelve monthly payments). Payment is made on-chain, in USDC, to Lectern's receiver address 0x14060A0838d2351799AbF3e92C4D148B091373ce on Base, Ethereum, Optimism, or Arbitrum. Pay from a wallet you have verified on your Farcaster profile — the app credits Pro by matching the sender against your verified addresses, so a payment from an unverified wallet won't be picked up.

🖥️ App — three entry points all open the wallet panel pre-filled with a 6.9 USDC send to the receiver on Base. You still press send yourself (confirmed with Touch ID) — Lectern never auto-broadcasts. Pro then unlocks once the on-chain verifier sees the payment (automatically on its next check, or immediately if you run a manual scan):

  • Settings → License → "Come back to Pro" — shown after you previously chose the free tier following a lapse.
  • The lapse choice modal — appears once, at the moment Pro lapses: "Top up Pro →".
  • The gifted-Pro reminder banner — shown when a launch-code grant is nearing expiry: "set up renewal".

The same flow is reachable from Settings → License → Payment instructions if you prefer to send manually from a different wallet. Lectern's verifier scans the chain for your payment automatically every 24 hours; press scan for payments now for an instant check.

flowchart LR
    A[Settings → License] --> B[Top up Pro →]
    B --> C[Wallet panel pre-filled · 6.9 USDC · Base · receiver]
    C --> D{Confirm with Touch ID}
    D -->|Confirmed| E[USDC sent on-chain]
    E --> F[Verifier scans chain every 24h · or Scan now]
    F --> G[Pro unlocked · stamp off]

⌨️ CLI

$ lectern license status                       # current state + whether the stamp applies
$ lectern license activate path/to/license.json   # manual JSON activation

Stake $KEYNOTE for a Pro discount

If you hold $KEYNOTE (Lectern's token on Base), you can stake it for a standing discount on Pro — or, at the top tiers, treat the stake itself as your Pro entitlement.

🖥️ AppSettings → License → Stake KEYNOTE for Pro discount. The panel is visible whether or not you are currently Pro, and doubles as your staking dashboard. It surfaces:

  • Header chip — the short $KEYNOTE contract address on Base, with copy and open-on-Basescan buttons.
  • Wallet balance row — your live $KEYNOTE balance with a [max] button that fills the stake input with your full balance.
  • Current stake line — e.g. "Currently staking 27M KEYNOTE · tier 1 · 15% off Pro USDC", or a prompt if you are below the minimum.
  • Stake tier gauge — a circular gauge (T0 / T1 / T2 / T3) with a progress ring toward the next tier, and the gap remaining ("23M to Tier 2") or a "Top tier · 40% off Pro" confirmation.
  • Stake / unstake toggle and amount input — broadcasts on Base using your custody key, gated by Touch ID.

The discount ladder:

Tier $KEYNOTE staked Discount on Pro USDC
Tier 1 25M 15% off
Tier 2 50M 25% off
Tier 3 100M 40% off

You can unstake at any time — there is no lockup and no penalty. Your tier is recomputed from your current stake on every action.

Machine-global Pro across all accounts

Pro is machine-global. When any account on your Mac pays for Pro, every account on that Mac is treated as Pro.

🌐 From farcaster.xyz: this is one of the biggest wins for multi-account users. farcaster.xyz bills per account; Lectern unlocks every account on the Mac for a single 6.9 USDC payment.

Pay once with your personal account, then switch to your dev, bot, or second account with ⌘2 — the sidebar tools stay unlocked, Snap Maker stays open, and the cast stamp stays off. Pro auto-expires when the latest paid-through date passes; no payment means no extension.

Lapse behavior

There is no lockout ladder. Lectern's sovereignty promise is absolute: writes always work, and the app always launches, paid or not. When Pro lapses, the only thing that changes is a banner and the return of the cast stamp — gentle pressure, never a wall.

flowchart TD
    A[Pro lapses] --> B[Day 0–1: red banner · cast stamp re-applies]
    B --> C[Day 2: yellow warning banner]
    C --> D[Day 3+: banner quiets · reminder lives in Settings → License]
    A -.writes always work.-> W[You can keep casting]
    A -.launch always works.-> L[App always opens]
  • Day 0–1 — a red banner appears, and the cast stamp re-applies. Pro features lock.
  • Day 2 — the banner turns yellow (a milder warning).
  • Day 3 and after — the banner quiets entirely; the renewal reminder lives only in Settings → License, so you can return whenever you choose.

What the cast stamp does

On the free tier (after the trial, with no subscription), every cast you broadcast gets a short footer auto-appended:

\n\nSent via Lectern on macOS

This is the cast stamp — a literal 27-byte string (two newlines plus the text). It carries no telemetry and no analytics ping; it is the same lightweight attribution pattern Neynar uses.

If your text plus the stamp would exceed Farcaster's byte ceiling, the CLI refuses with a clear "shorten by N bytes or subscribe" message rather than truncating. In a thread, only the last cast in the chain is stamped.

License state file

Your trial and subscription state lives at:

~/Library/Application Support/Lectern/license.json

If you reinstall macOS, copy this file across, or simply re-activate from your purchase email.

Recovery and backup

Two different operations share this section. Keep them straight:

  • Recovery rotates a compromised custody key to a fresh one, using your recovery key. Your Farcaster identity (your FID) stays the same.
  • Backup saves an encrypted snapshot of your keys so you can restore them on another Mac.

⌨️ CLI handles key backup and recovery. 🖥️ App handles cast and direct-cast archives (file paths below).

flowchart TD
    subgraph Recovery
    R1[Custody key compromised] --> R2[lectern recover name --send]
    R2 --> R3[Fresh custody key, same FID]
    end
    subgraph BackupRestore[Backup and Restore]
    B1[lectern backup name] --> B2[Encrypted .lectern-backup file]
    B2 --> B3[lectern restore file · on another Mac]
    end

Archive folder

Every user-facing export the app produces — cast archives, direct-cast archives, scheduled-Space .ics calendar files, exported keys, custom themes, and snap manifests — defaults to one canonical location:

~/Documents/Lectern/Archives/

The folder is created on first export with owner-only permissions (rwx------, i.e. mode 0700), so other users on the same Mac cannot read its contents. If an older version left the folder with looser permissions, Lectern tightens them on the next export.

Before opening the save panel, the app may gate on Touch ID — you authenticate, then Lectern presents the file dialog. If your Mac has no Touch ID, or you decline, the save panel opens normally without the gate. The panel starts in the archives folder by default; you can navigate elsewhere for a one-off export.

Back up

A backup is a passphrase-encrypted file containing your account's keys. Choose a passphrase of at least 8 characters; it is typed hidden and asked for twice.

$ lectern backup myaccount
backup passphrase (>=8 chars, hidden):
confirm passphrase:

backup written: ~/Lectern-Backups/myaccount-1778192345.lectern-backup

Store the file and the passphrase off-machine. Without the passphrase, the file is useless — to you and to anyone who finds it.

Restore on another Mac

$ lectern restore ~/Lectern-Backups/myaccount-1778192345.lectern-backup
backup passphrase (hidden):

restored: myaccount

Lectern verifies that the decrypted keys derive to the addresses the file claims — a wrong passphrase or a tampered file fails cleanly. By default, restore refuses to overwrite an existing local account of the same name; add --force to override:

$ lectern restore ~/Lectern-Backups/myaccount-1778192345.lectern-backup --force

Recover (custody compromised)

If your custody key is compromised, recovery moves your FID to a brand-new custody key while keeping your identity. It signs the transfer with the recovery key already in your Keychain.

$ lectern recover myaccount --send

Without --send this runs as a dry-run (it shows what would happen but broadcasts nothing). See Profile, identity, verifications → Recover a compromised custody for the full procedure, including the pre-condition that the on-chain recovery address must match your Keychain recovery key.

🔒 Recovery and restore each require both Touch ID and a TOTP code — see Two-factor auth.

Multi-account management (CLI)

⌨️ CLI only. If you run several Farcaster accounts — bots, scheduled publishers, drop tools — Lectern speaks a Terraform-style config-as-code dialect: you describe the desired state of each account in a file, and Lectern makes the chain match it.

The same write pipeline backs the macOS app, the CLI, and config-as-code, so there is no separate code path that could quietly diverge between the app and the command line.

flowchart LR
    A[lectern import · scaffold config] --> B[Edit lectern.toml]
    B --> C[lectern plan · preview changes]
    C --> D{Changes?}
    D -->|exit 0| E[No-op · matches chain]
    D -->|exit 2| F[lectern apply --auto-approve]
    D -->|exit 1| G[Error]
    F --> H[On-chain state matches config]

Scaffold a config from current state

$ lectern import myaccount [other-account …] -o lectern.toml

This pulls each named account's profile, follows, blocks, and verifications from the Hub and writes them into a file you can edit.

Preview changes (plan)

Edit the file, then preview what Lectern would do — without touching the chain:

$ lectern plan -f lectern.toml
Plan: 4 change(s) for `myaccount` (FID 3325849)
  ~ profile.bio
      currently on-chain: "old bio"
      config will set:    "new bio"
  + follow 6131      (config adds)
  …

Exit codes follow Terraform: 0 means no changes, 2 means changes are pending, and 1 means an error occurred. Add --json to pipe the result into other tools.

Drift

Drift is when the live, on-chain state no longer matches your config — usually because you made a manual change in another client. Once an account is config-managed, the config is the source of truth: the next apply reverts any drift, and plan flags it loudly.

If a manual change was deliberate and you want to keep it, re-scaffold the config from the current state:

$ lectern import myaccount -o lectern.toml   # rewrite config from current state
$ lectern plan -f lectern.toml               # confirms clean

Apply

To apply means to execute the pending changes that plan previewed.

$ lectern apply -f lectern.toml --auto-approve

Without --auto-approve, Lectern prompts you to confirm before broadcasting.

Verification adds are intentionally skipped. Verifying an address requires the private key of the address being verified, which never goes into a config file. Run verify-address or verify-solana separately for those. Verification removes apply normally.

Fleet observability

$ lectern status --all
$ lectern status --all --filter bot-          # name-prefix filter
$ lectern status --all --json | jq '.accounts[]'

Exits 0 when every account is healthy, 1 if at least one errored.

Headless and CI

A continuous-integration runner has no human to answer a Touch ID prompt, and needs a macOS host (for Keychain access). Set the bypass environment variable:

export LECTERN_UNSAFE_NO_2FA=1

Or disable two-factor auth for a single service account, persisted across runs:

$ lectern auth disable-account bot-events

Both bypasses are deliberately loud — every gated call writes a warning to stderr, so it is visible in your CI logs.

Two-factor auth (CLI)

⌨️ CLI is where you set up two-factor auth. The macOS app uses Touch ID directly and needs no separate enrollment.

Lectern gates sensitive actions behind Touch ID / Face ID by default. A second factor, TOTP — a Time-based One-Time Password, the rotating six-digit code from an authenticator app like Google Authenticator — is opt-in for most commands, but is always required for the headless recover, restore, and apply flows (which have no biometric prompt of their own).

Default factor policy

The guiding rule is simple: every action that signs something or moves money on-chain requires Touch ID. That includes all $KEYNOTE transactions, mini-app signing, and revealing your seed phrase. Reads and purely local operations require nothing.

Op category Examples Default factors
Reads status, casts, feed, notifications None
Local-only mute, keys list, plan None
Standard writes cast, uncast, thread-cast, react, follow, block, profile.set, verify-address, verify-solana, verify-remove, claim-fname, rent-storage, bridge, tx, siwf, user-data Touch ID
Wallet send wallet-send — any token transfer from Wallet → Send Touch ID
Airdrop airdrop — per-chain Disperse calls signed with your custody key Touch ID
Schedule schedule — biometric at schedule time, so deferred sends inherit your consent Touch ID
Engagement queue engagement-queue — Boost Like/Recast/Quote queued for later Touch ID
$KEYNOTE on-chain moves keynote-boost, keynote-claim, keynote-stake, keynote-unstake Touch ID
Mini-app signing & batch tx mini-app-sign (personal_sign / typed-data), tx-batch (batch transactions) Touch ID
Snap authoring & submit snap.card, snap.poll, snap.vote, snap.claim, snap.verify, snap.mini-app-launch, snap.submit Touch ID
Key & signer ops keys.show-mnemonic (reveals seed phrase), keys.import-mnemonic, keys.delete, batch-revoke-signers, miniapp.manifest Touch ID
Identity-changing register, register-signer, register-auth-address, change-recovery-address, revoke-signer Touch ID
Headless / existential recover, restore, apply Touch ID + TOTP

lectern auth status prints the policy currently in force.

🔒 Why deferred sends gate at queue time. schedule and engagement-queue ask for biometric the moment you queue them. The background runner fires later with auth bypassed — because by then the daemon has no window to prompt you. Your explicit authorization happens up front; the later fire is just execution.

💸 Why wallet send is gated. Earlier builds let wallet sends through without a prompt, because wallet-send was missing from the default policy and the policy fell through to "no factors" on unknown commands. The fix added the entry and a fallback in the policy resolver, so users with an older auth.json get the gate without losing their custom settings. The same class of fix later closed the gap for the $KEYNOTE money moves, mini-app signing, and batch transactions.

Enroll TOTP

$ lectern auth totp setup

This prints an otpauth:// URL. Scan it into Google Authenticator, 1Password, Authy, or Bitwarden, then confirm the pairing with a code from the app:

$ lectern auth totp verify 287082

Lectern accepts a drift of ±1 thirty-second period (about ±30 seconds) to tolerate small clock differences between your Mac and your phone.

sequenceDiagram
    participant U as You
    participant L as lectern CLI
    participant A as Authenticator app
    U->>L: lectern auth totp setup
    L-->>U: prints otpauth:// URL
    U->>A: scan URL
    A-->>U: shows 6-digit code
    U->>L: lectern auth totp verify 287082
    L->>L: check code (±1 period drift)
    L-->>U: TOTP enrolled ✓

Customize

# Require BOTH factors for every cast (paranoid mode)
$ lectern auth policy set --command cast --factor biometric --factor totp

# Make `follow` 2FA-free
$ lectern auth policy set --command follow --factor none

# Reset to defaults
$ lectern auth policy reset

Bypass for scripts

$ LECTERN_UNSAFE_NO_2FA=1 lectern apply -f config.toml --auto-approve
warning: LECTERN_UNSAFE_NO_2FA bypassed 2FA for `apply`

The warning prints to stderr on every call. Use it sparingly.

Disable TOTP

$ lectern auth totp disable

Afterward, any command that requires TOTP fails cleanly. Re-enroll with lectern auth totp setup.

Bookmarks

🖥️ App — sidebar Tools → Bookmarks. Save casts you want to return to later.

⌨️ CLI

$ lectern bookmark add lectern-dev "3:0xd658…" --note "great recovery thread" -t farcaster -t ref
$ lectern bookmark list lectern-dev --tag ref
$ lectern bookmark show lectern-dev "3:0xd658…"        # hydrates the cast text from the Hub
$ lectern bookmark export lectern-dev --format md -o ~/bookmarks.md

Bookmarks are local-only. Farcaster has no on-chain bookmark primitive, so your bookmarks live on your machine and are never broadcast. They are stored at:

~/Library/Application Support/Lectern/bookmarks.json

Snaps

Snaps are interactive Farcaster embeds — small cards with buttons (polls, tip jars, claim CTAs, and so on) that render inline in a cast. Lectern both publishes snaps and renders other people's snaps natively in your feed.

Publish (App) 🖥️

Open sidebar Tools → Snap Maker (shortcut ⌘⇧S). Pick one of the 14 templates — Profile card, Linktree, Announcement, Event, Tip jar, Quote, Poll, AMA, Claim, Verify, Mini-app launcher, Tip, Leaderboard, RSVP — fill in the form, preview live, then press Create + open composer to publish and seed a fresh composer with the URL.

Publish (CLI) ⌨️

$ lectern snap card myaccount --pin
$ lectern snap poll myaccount --question "?" --option "A" --option "B" --pin
$ lectern snap claim myaccount --title "…" --cta "…" --url "…"
$ lectern snap verify myaccount --subject "…" --address 0x…
$ lectern snap mini-app-launch myaccount --mini-app-name "…" --image-url … --launch-url …

The --pin flag makes the stable URL https://devflair.xyz/p/<account>/<kind> resolve to this snap.

Vote on a poll

Use the native button in the app feed, or the CLI:

$ lectern snap vote a65794d3333f --account myaccount --choice 2

The vote is cryptographically signed with your account's signer. The hosting worker verifies that signature against your on-chain key registry before accepting it, so votes cannot be forged. One vote per FID per poll; voting again replaces your previous choice.

sequenceDiagram
    participant U as You
    participant L as lectern
    participant W as Snap worker
    U->>L: snap vote <slug> --choice 2
    L->>L: sign vote with account signer
    L->>W: POST signed vote
    W->>W: verify signature vs on-chain key registry
    W-->>U: vote recorded (1 per FID, replaces prior)

Smoke-test every template

To confirm all templates round-trip against the live worker:

$ lectern snap smoke --account myaccount

Troubleshooting

Keychain prompts on every command

You are likely running an unsigned build. Build with make (not bare swift build) — the Makefile signs with a stable identifier, so macOS's Always Allow choice persists across rebuilds.

In production (a signed, notarized DMG), Always Allow persists across launches and across signed updates that share the same Developer ID and bundle identifier.

Keychain admin-password prompt during background scheduler install

The first time you install the background scheduler — Settings → Developer → install background scheduler, or lectern scheduled install from a terminal — macOS may show a one-time admin password prompt.

That prompt is a legacy Keychain access-control migration. Older access rules (created before Lectern v0.2.0) bind your signer key to one specific binary; the scheduler the system launches in the background is technically a different binary, so without the migration it would prompt at every scheduled-cast fire — which a background process can't answer, stalling the cast. The migration runs at install time, while you are at the keyboard. Enter your admin password once; later fires are silent forever.

If you previously approved the migration but still see prompts when a scheduled cast fires, check three things:

  1. ~/Library/Application Support/Lectern/accounts.json — every account should show "signerACLVersion": 2. If any are stuck at 1, the migration failed silently for that account. Run the install button again; it is idempotent.
  2. The LaunchAgent points at the bundled CLI. Run launchctl print gui/$(id -u)/studio.shyguy.lectern.scheduler | grep BundleProgram — it should show Contents/Helpers/lectern. If it shows a Homebrew path or a debug build, uninstall and re-install via the GUI button (which prefers the bundled CLI).
  3. Last exit status. launchctl print gui/$(id -u)/studio.shyguy.lectern.scheduler | grep "last exit"64: EX_USAGE means you are on a build before the argv[0] fix; re-install from a fresh DMG.

Background scheduler installed but says "requires approval"

Registration succeeded, but macOS has parked the agent until you flip a toggle in System Settings. The install button surfaces a clear message and opens System Settings → General → Login Items & Extensions → Background for you. Turn Lectern on under "Allow in the Background." Back in Settings → Developer, the toggle flips to ON automatically once macOS recognizes the approval.

lectern doctor fails on a contract

Farcaster has migrated contract addresses before. Confirm the current address via the Bundler's view methods, update Sources/LecternCore/FarcasterContracts.swift, and rebuild.

"no signer for X — run lectern register-signer X first"

You have not registered a signer for that account, or you revoked it. Run lectern register-signer <name> (it broadcasts by default; add --dry-run only to preview).

Optimism RPC errors ("transaction underpriced", rate limits)

Your LECTERN_OP_RPC endpoint is probably hitting a free-tier rate limit. Switch RPC keys or wait a minute.

My fname disappeared after register

It didn't. register only registers the FID on chain; the fname is a separate step. Run lectern claim-fname <name>.

recover says "on-chain recovery doesn't match Keychain"

Someone changed the recovery address on chain (via IdRegistry.changeRecoveryAddress), so the recovery key in your Keychain is no longer authorized. If you did not make that change, treat it as a security incident.

A cast won't broadcast: HTTP 400

The most common cause is exceeded storage for that store type. Check lectern storage <name>. If casts used equals the limit, rent more with lectern rent-storage <name> --send.

apply skipped my verifications

That is intentional. Verifying an address needs the private key of the address being verified, which is not in your config. Run verify-address or verify-solana separately.

Snap vote rejected with "signed message outside ±5min window"

Your Mac's clock is too far off. Check that network time (NTP) is enabled. The vote server enforces a ±5-minute replay window.

Mini app doesn't show my profile

Usually a cold-start race. Re-open the mini app once — Lectern retries the Hub fetches with backoff, and the second open inherits the warmed pipeline.

Cashtag card says "not indexed by DexScreener"

The token is not yet indexed on either GeckoTerminal or DexScreener — common for brand-new launches or low-liquidity tokens. The fallback card still surfaces a Basescan / Etherscan / Solscan link so you can verify the contract on the right block explorer. Full price data appears automatically once either source picks the token up; nothing to do on your end.

Spaces mic toggle fails with AudioEngine Error -9000

Another app holds your microphone exclusively. Common culprits: a Safari tab with Spaces open, Zoom, Discord, FaceTime, OBS, QuickTime. Quit it; Lectern auto-resets the audio engine and retries once on your next mic tap. If the retry also fails, the inline error lists the next steps — restarting Lectern is usually enough, and occasionally a full Mac restart is needed if the system audio daemon got wedged.

CLI reference

Common commands

Command What
keys new <name> Generate custody + recovery keys
keys list List local accounts
keys show <name> Show addresses
keys delete <name> [--yes] Permanently delete an account's keys
register <name> [--dry-run] Register the FID on chain (sends by default)
claim-fname <name> [--fname <x>] Claim an fname
register-signer <name> [--dry-run] Register an Ed25519 signer (sends by default)
revoke-signer <name> [--dry-run] [--clear-keychain] [--key <hex>] Revoke a signer (sends by default)
recover <name> [--send] Rotate custody using your recovery key
profile show <target> Read a profile
profile set <name> --display "…" --bio "…" … --send Set profile fields
verify-address <name> [--privkey …] --send Verify an Ethereum address
verify-solana <name> --privkey … --send Verify a Solana address
verify-remove <name> --address … [--protocol …] --send Un-verify
cast <name> "<text>" [--reply …] [--channel …] [--embed* …] --send Compose
uncast <name> <fid:hash> --send Delete a cast
thread-cast <name> --text-file post.md --send Long-form reply chain
follow <name> <target> [--remove] --send Follow / unfollow
react <name> --cast/--url --type like|recast [--remove] --send React
block <name> <target> [--remove] --send Block / unblock
mute <name> <target> / mute <name> --list Local mute
social stats / mutuals / one-way / orphans <name> Follow-graph queries
social follow-back <name> --send / social prune <name> --send Bulk follow ops
storage <name> / rent-storage <name> --units N --send Storage
casts <target> [--limit N] Read someone's casts
feed <name> [--limit N] Aggregated feed
notifications <name> [--types …] [--since ts] Notifications
thread <fid:hash> [--depth N] Cast + reply tree
signers <target> / verifications <target> On-chain reads
status <name> / status --all Account overview / fleet view
plan -f <config> / apply -f <config> --auto-approve Config-as-code
import <name> [...] -o file [--format toml|json] Scaffold config
export <name> [--out file] [--format json|md] Account snapshot
backup <name> / restore <file> [--force] Key backup / restore
snap card / poll / claim / verify / mini-app-launch <name> Author a snap
snap vote <slug> --account <name> --choice N Signed poll vote
snap smoke --account <name> Round-trip-test every template
bookmark add / list / show / remove / note / tag / export <name> Bookmark ops
license status / license trial start / license activate <file> License
auth status / auth touchid-test / auth totp setup 2FA setup
auth policy set / reset Per-command factor config
auth disable-account <name> / auth enable-account <name> Per-account bypass
identify --address | --privkey | --mnemonic … Match key material to an FID
fid <N> Look up a custody + recovery for FID N
doctor Verify Farcaster contracts are deployed
completion bash | zsh | fish Shell completion
--version Print version

Environment variables

Var Purpose Required
LECTERN_OP_RPC Optimism Mainnet RPC URL Yes
LECTERN_ETH_RPC Ethereum Mainnet RPC URL Only for Ethereum reads (e.g. Pro payment scan)
LECTERN_BASE_RPC Base Mainnet RPC URL Only for Base ops (Pro payment, staking)
LECTERN_ARB_RPC Arbitrum Mainnet RPC URL Only for Arbitrum reads
LECTERN_SOL_RPC Solana RPC Only for Solana ops
LECTERN_SNAP_BASE_URL Snap server base URL Defaults to https://devflair.xyz
LECTERN_SNAP_INGEST_SECRET Bearer for a self-hosted snap server Only if self-hosting
LECTERN_UNSAFE_NO_2FA Set to 1 to bypass 2FA (CI). Prints a stderr warning. No — use sparingly

File locations

Path What
~/Library/Application Support/Lectern/config.env User-global config
~/Library/Application Support/Lectern/accounts.json Account state cache (incl. signer ACL version per account)
~/Library/Application Support/Lectern/mutes.json Local mute lists
~/Library/Application Support/Lectern/blocks.json Local block lists
~/Library/Application Support/Lectern/auth.json 2FA policy
~/Library/Application Support/Lectern/license.json Trial + subscription state
~/Library/Application Support/Lectern/subscription.json Per-FID subscription records
~/Library/Application Support/Lectern/bookmarks.json Bookmarks (per-account)
~/Library/Application Support/Lectern/drafts.json Composer drafts (per-account, auto-saved every 2s + on close)
~/Library/Application Support/Lectern/cashtag-directory.json Shared cashtag entries (ticker, chain, address, name, symbol, last verified)
~/Library/Application Support/Lectern/scheduled-casts.<account>.json Scheduled casts per account (read by the background scheduler when installed)
~/Library/Application Support/Lectern/wallet-recipients.<account>.json Recent recipients ring buffer (per-account, 20 entries)
~/Library/Application Support/Lectern/analytics.<fid>.json Cast Analytics cache (per-FID; resumable scan)
~/Library/Application Support/Lectern/engagement-budget.<fid>.json Boost engagement budget tracker (per-FID)
~/Library/Application Support/Lectern/channels-followed.json Channels you've followed locally
~/Library/Application Support/Lectern/miniapps.json Mini apps added to the sidebar
~/Library/Application Support/Lectern/dc-archive/<account>/<conversationId>.jsonl Opt-in local DC archive (per-account, AES-GCM sealed)
~/Library/Application Support/Lectern/archive/<account>.jsonl Local cast archive (per-account, JSONL append-only)
~/Documents/Lectern/Archives/ User-facing exports (cast archives, DC archives, .ics calendars, themes, key exports). Owner-only permissions; optional Touch ID gate.
~/Lectern-Backups/<name>-<ts>.lectern-backup Backup files (default location for lectern backup)
~/Library/LaunchAgents/studio.shyguy.lectern.scheduler.plist Background scheduler launchd agent (managed by SMAppService)
macOS Keychain studio.shyguy.lectern Custody / recovery / signer private keys
macOS Keychain studio.shyguy.lectern.totp TOTP shared secret
macOS Keychain studio.shyguy.lectern.spaces.bearer Farcaster Spaces session bearer captured from the Connect WebView (plus a .meta sibling for metadata)

Exit codes

Code Meaning
0 Success / no-op
1 Error
2 plan only: changes pending (Terraform convention)