Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
8aa5732
Give Go and Git Bash the same file for /tmp paths on Windows
JeremiahM37 Sep 28, 2026
d3524f0
Keep the smoke test's lectern.exe out of the test's temp directory
JeremiahM37 Sep 28, 2026
db41479
Accept older Python wording for trailing-comma JSON errors in the par…
JeremiahM37 Sep 28, 2026
1e7243f
Merge branch 'connect-ui' into ptyhost-win
JeremiahM37 Oct 2, 2026
1fef17d
Round two of the simpler web app: installed agents only with the demo…
JeremiahM37 Oct 2, 2026
c465418
Refuse agents that aren't installed, expire approvals with their sess…
JeremiahM37 Oct 2, 2026
fc68c6d
Keep a server's database in the state directory, and make local statu…
JeremiahM37 Oct 2, 2026
c9e9ea8
Let a phone on this Wi-Fi connect in one click, and add lectern phone
JeremiahM37 Oct 2, 2026
bc87936
One Getting Started page, and docs that match the product
JeremiahM37 Oct 2, 2026
3916550
Keep a session's secrets off its command line and off a dead agent's …
JeremiahM37 Oct 2, 2026
26c7672
Attach without tmux: Lectern draws the session with the same key bar,…
JeremiahM37 Oct 2, 2026
8ac562c
Plain words left over from the re-audit: no project, work in progress…
JeremiahM37 Oct 2, 2026
72fd2be
Keep chat anchored, follow renames, and fit the calmer Sessions page …
JeremiahM37 Oct 2, 2026
9270d85
Check the agent is installed before Revive closes the old terminal
JeremiahM37 Oct 2, 2026
1775a1e
Dashboard e2e reads the described project, not its record
JeremiahM37 Oct 2, 2026
ef49ce2
Say a missing git name and email before the folder moves, not after
JeremiahM37 Oct 2, 2026
7ed44cc
Merge branch 'simple-core-3' into connect-ui
JeremiahM37 Oct 2, 2026
482aa11
Merge branch 'simple-tui-2' into connect-ui
JeremiahM37 Oct 2, 2026
22c298b
Merge branch 'simple-web-2' into connect-ui
JeremiahM37 Oct 2, 2026
88ff1e6
Merge branch 'ptyhost-win' into connect-ui
JeremiahM37 Oct 2, 2026
3a0b307
Rebuild the web bundle after merging the round-two simplicity, termin…
JeremiahM37 Oct 2, 2026
23ab1ee
Never end a session because a different backend cannot see it
JeremiahM37 Oct 2, 2026
39bc5d3
Merge origin/main into connect-ui: one first-session, phone and navig…
JeremiahM37 Oct 2, 2026
ece6193
Keep the full suite steady at 24 CPUs
JeremiahM37 Oct 2, 2026
814c5b5
Follow the device's light or dark setting by default
JeremiahM37 Oct 2, 2026
d97f91e
Move internal process records under docs/internal
JeremiahM37 Oct 2, 2026
67f77d3
Replace homelab specifics in user docs with generic examples
JeremiahM37 Oct 2, 2026
2467966
Lead the README with a quick start for each OS
JeremiahM37 Oct 2, 2026
c7d5483
Give the desktop sidebar the phone's three items and a More group
JeremiahM37 Oct 2, 2026
c214184
Walk a new user through a whole first session in Getting started
JeremiahM37 Oct 2, 2026
0d0535e
Commit tab: ask for a git name and email inline, draft a message only…
JeremiahM37 Oct 2, 2026
2fa0671
Show an approval's command once, under a plain heading
JeremiahM37 Oct 2, 2026
5b98a4b
Check the README's version badge only when it has one
JeremiahM37 Oct 2, 2026
2aa9461
Keep keys pressed while the attach client takes the Windows console back
JeremiahM37 Oct 2, 2026
bd5ca2c
Run the native PTY tests from one workflow and gate releases on them
JeremiahM37 Oct 2, 2026
af367b2
Accept a git name and email with a commit instead of sending people t…
JeremiahM37 Oct 2, 2026
8d65dce
Ask for a git name and email in the terminal commit form
JeremiahM37 Oct 2, 2026
3817062
Show a pending approval above the web terminal, with Y, A and N keys
JeremiahM37 Oct 2, 2026
973c9cf
Offer to find tmux sessions only where tmux is installed
JeremiahM37 Oct 2, 2026
03ca735
Fall back to the suggested commit message when a draft is not one
JeremiahM37 Oct 2, 2026
1e87ecf
Deny with n on an asking session and offer tmux discovery only where …
JeremiahM37 Oct 2, 2026
c8a6c40
Open the saved-conversation picker straight from lectern restore
JeremiahM37 Oct 2, 2026
41478de
Add lectern demo and point there when no agent is installed
JeremiahM37 Oct 2, 2026
2416ffc
Suggest the command people mean by pair, resume and similar words, an…
JeremiahM37 Oct 2, 2026
3fe00c3
Format the demo command and the synonym table
JeremiahM37 Oct 2, 2026
ab3e664
Chat: keep the header to name, status and close; put usage, tests, ag…
JeremiahM37 Oct 2, 2026
47ab82a
List n deny and the attached approval keys in the terminal help
JeremiahM37 Oct 2, 2026
696c238
Merge branch 'simple-docs' into connect-ui
JeremiahM37 Oct 2, 2026
a368b46
Commit tab: show a refused name or email under the fields, credit fal…
JeremiahM37 Oct 2, 2026
28814dd
Use the server's tmux_installed to decide whether to offer finding tm…
JeremiahM37 Oct 2, 2026
9c4693f
Browser tests for the simpler desktop sidebar, chat ⋯, commit result,…
JeremiahM37 Oct 2, 2026
4c13e2a
Settings: quiet the advanced section tabs so Basics leads
JeremiahM37 Oct 2, 2026
99b5234
Merge branch 'simple-fe' into connect-ui
JeremiahM37 Oct 2, 2026
e04df56
Rebuild the web bundle after merging the simpler sidebar, commit and …
JeremiahM37 Oct 2, 2026
2410309
Check the socket guard in child processes without /bin/sh, so it runs…
JeremiahM37 Oct 2, 2026
4ae6fc6
Open commit Options before checking push, and check the quickbar in d…
JeremiahM37 Oct 2, 2026
063011d
Make the socket guard and plugin tests independent of drive paths, di…
JeremiahM37 Oct 2, 2026
b857877
Wait for the requeued hold in the account fallback test, and describe…
JeremiahM37 Oct 2, 2026
deba06a
Have the attach-test popup report through a file, not the screen
JeremiahM37 Oct 2, 2026
83a5a96
Release 2.7.0
JeremiahM37 Oct 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
34 changes: 4 additions & 30 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,37 +90,11 @@ jobs:
shell: pwsh
run: ./tests/check-powershell-client.ps1

# The PTY host is the session backend on Windows and macOS
# (docs/ptyhost.md). These machines are disposable, so the tests that start
# real programs run directly; on Linux they run in the reviewed isolated
# runner above. The smoke test builds lectern, serves it, opens a shell,
# types through the API and the web terminal, restarts the server and finds
# the same shell.
# The PTY host is the session backend on Windows and macOS; these are the
# native tests for it (pty-backend.yml). The release workflow runs the same
# jobs and will not publish unless they pass.
pty-backend:
strategy:
fail-fast: false
matrix:
os: [windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.25"
cache: true
- name: Session backend, PTY host, web terminal and server smoke tests
shell: bash
env:
LECTERN_PTYHOST_TESTS: "1"
LECTERN_SERVER_SMOKE: "1"
run: >-
go test -count=1 -v
./internal/sessions/backend/
./internal/ptyhost/...
./internal/terminal/webterm/
./internal/gitbash/
./internal/smoke/
./cmd/lectern/localruntime/
uses: ./.github/workflows/pty-backend.yml

e2e:
runs-on: ubuntu-latest
Expand Down
48 changes: 48 additions & 0 deletions .github/workflows/pty-backend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: PTY backend

# The PTY host is the session backend on Windows and macOS
# (docs/ptyhost.md). These machines are disposable, so the tests that start
# real programs run directly; on Linux they run in the reviewed isolated
# runner (ci.yml). The smoke test builds lectern, serves it, opens a shell,
# types through the API and the web terminal, restarts the server and finds
# the same shell. The attach-client test drives Lectern's own key bar and
# Ctrl+] controls on a real ConPTY / pseudo-terminal, without tmux.
#
# Called by ci.yml on every push and pull request, and by release.yml, which
# does not publish anything unless both platforms pass.
on:
workflow_call:
workflow_dispatch:
permissions:
contents: read
jobs:
pty-backend:
strategy:
fail-fast: false
matrix:
os: [windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.25"
cache: true
- name: Session backend, PTY host, web terminal and server smoke tests
shell: bash
env:
LECTERN_PTYHOST_TESTS: "1"
LECTERN_SERVER_SMOKE: "1"
run: >-
go test -count=1 -v
./internal/sessions/backend/
./internal/ptyhost/...
./internal/terminal/webterm/
./internal/gitbash/
./internal/smoke/
./cmd/lectern/localruntime/
- name: Attach client without tmux
shell: bash
env:
LECTERN_PTYHOST_TESTS: "1"
run: go test -count=1 -v -run '^TestBareAttachmentControls' ./cmd/lectern/
9 changes: 7 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@ jobs:
first-install:
uses: ./.github/workflows/first-install.yml

# Windows and macOS run on the PTY host; nothing ships unless its native
# tests pass on both.
pty-backend:
uses: ./.github/workflows/pty-backend.yml

binaries:
needs: first-install
needs: [first-install, pty-backend]
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
Expand Down Expand Up @@ -90,7 +95,7 @@ jobs:
/tmp/instcheck/lectern version | grep -q "${GITHUB_REF_NAME#v}"

image:
needs: first-install
needs: [first-install, pty-backend]
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
Expand Down
6 changes: 3 additions & 3 deletions .verify.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ steps:
- wait: 2.5
expect:
vision: >
the Lectern web app with a desktop sidebar showing Sessions, Approvals, Tasks, Terminals
and Settings directly, with no More flyout, and a list of agent sessions or a clear "Start an agent"
action; no error text, no blank white screen
the Lectern web app with a short desktop sidebar whose main items are Sessions, Approvals and
Settings plus a "More" group (Tasks or Terminals may also appear while in use), and a list of
agent sessions or a clear "Start an agent" action; no error text, no blank white screen

- name: tasks board renders live
actions:
Expand Down
117 changes: 82 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,89 @@

# Lectern

**The self-hosted control plane for coding agents.**
**Lectern runs coding agents such as Claude Code, Codex and Gemini CLI on your
own computer, and lets you follow them, approve what they do and review their
changes from your terminal, browser or phone.**

Run Claude Code, Codex, Gemini CLI and other agents across your workstation,
SSH servers and disposable sandboxes. Dispatch work, choose where it runs,
and supervise it from your terminal or phone.

![version](https://img.shields.io/github/v/release/JeremiahM37/lectern)
[![Latest release: v2.7.0](https://img.shields.io/github/v/release/JeremiahM37/lectern?label=release&color=8b5cf6)](https://github.com/JeremiahM37/lectern/releases/latest)
![license](https://img.shields.io/badge/license-MIT-blue)
![go](https://img.shields.io/badge/single%20binary-Go-00add8)

</div>

```bash
## Quick start

You need [Git](https://git-scm.com/downloads) and, for real work, an agent CLI
such as Claude Code, Codex or Gemini CLI. Without one you can still try Lectern
with its built-in demo agent.

**macOS and Linux**

```sh
curl -fsSL https://raw.githubusercontent.com/JeremiahM37/lectern/main/install.sh | sh
lectern up
```

[Linux](docs/getting-started-linux.md) · [macOS](docs/getting-started-macos.md) · [Windows](docs/getting-started-windows.md)
Or on macOS with Homebrew: `brew install JeremiahM37/tap/lectern`

**Windows** (PowerShell, with [Git for Windows](https://git-scm.com/download/win) installed)

```powershell
irm https://raw.githubusercontent.com/JeremiahM37/lectern/main/install.ps1 | iex
```

Or with Scoop: `scoop bucket add jeremiahm37 https://github.com/JeremiahM37/scoop-bucket && scoop install lectern`

**Then, in a project folder:**

```sh
cd ~/myapp
lectern claude # start your first agent here (or: lectern codex, lectern gemini)
lectern up # open the web dashboard in your browser
lectern phone # show a QR code to pair a phone on the same Wi-Fi
```

The agent keeps running when you leave the terminal (**Ctrl+]** then **d**),
and the same session shows up in the browser and on your phone. On a new
install the agent asks before risky actions, such as running a command or
editing a file, and you can answer from any of them.

**New to Lectern?** [Getting started](docs/getting-started.md) walks through
your first session: install, start an agent, approve from the browser and the
phone, then review and commit the change.

`lectern doctor` checks your setup and prints a fix for anything missing.
`lectern update` installs a new release. `lectern help` lists every command.

![Choose a machine, dispatch agent work, and review the result in Lectern](docs/media/control-plane/dispatch-review.gif)

[Watch the walkthrough](docs/media/control-plane/control-plane.mp4) · [Screenshots and recording details](docs/media/control-plane/README.md)

*Recorded from the current app with disposable demo projects and scripted agents.
The recording demonstrates the control workflow, not model performance or a live cluster.*
The recording demonstrates the workflow, not model performance.*

## What you can do

- **Watch and talk to agents.** Every session has a terminal and a chat view.
See which sessions are working, idle or waiting for you.
- **Approve or deny.** A pending request shows the command or the diff, with
**Allow once**, **Allow for this session** or **Deny**, on the desktop and
on the phone.
- **Review and commit.** **Review & merge** shows what the agent changed. Leave
comments for the agent, stage what you want and commit. On your main branch
it offers a new branch first.
- **Use your phone.** The phone layout is installable as an app, with
notifications when an agent needs you.

<img src="docs/media/control-plane/phone-approval.png" alt="A pending approval in the phone layout" width="300">

## Your agents. Your machines. One place to run the work.
[Terminal client](docs/terminal-client.md) · [Phone supervision](docs/mobile-sessions.md) · [Review](docs/review.md)

## Going further

Everything above runs on one computer. Lectern can also manage agents across
several machines.

### Run work where it belongs

A coding task needs somewhere to run, a workspace of its own, and a way to
bring you back when it needs a decision. Lectern connects those pieces across
Expand Down Expand Up @@ -59,20 +115,18 @@ runs its queued tasks there. Automatic placement by GPU, RAM or OS requirements
is a future direction, not a current feature. Worktrees separate changes;
use a sandbox when you also need execution isolation.

## Run work where it belongs

| Execution environment | What Lectern does |
|---|---|
| **Your workstation** | Runs the installed agent CLIs locally, with persistent terminals on Linux, macOS and Windows. |
| **SSH server or VPS** | Runs agents and manages workspaces remotely. Import SSH aliases, use jump hosts, and reconnect to sessions. |
| **Existing Proxmox LXC** | Executes through `pct` from the Proxmox host. |
| **Disposable sandbox** | Creates a Proxmox template clone, Docker container, or environment supplied by trusted script hooks for each attempt. Saves results before configured cleanup. |

[SSH machines](docs/ssh.md) · [Sandbox providers and lifecycle](docs/sandboxes.md) · [Isolation options and limits](docs/isolation.md)
[Run a shared server](docs/quickstart.md) · [SSH machines](docs/ssh.md) · [Sandbox providers and lifecycle](docs/sandboxes.md) · [Isolation options and limits](docs/isolation.md)

![Machines and projects in the current desktop UI](docs/media/control-plane/machines.png)

## Keep the agent choice yours
### Keep the agent choice yours

Claude Code, Codex and Gemini CLI are built in. Add OpenCode, Aider, Goose,
Cursor and other runners from the catalog, or configure a custom CLI. Agent
Expand All @@ -84,22 +138,19 @@ ended sessions when the agent has resumable history.

[Agent catalog and capabilities](docs/agents.md) · [Delegated builds](docs/DELEGATED_BUILDS.md) · [Replay evals](docs/replay-evals.md)

## Stay in control from your desk or phone

The terminal dashboard, desktop web app and installable phone PWA look at the
same work. See which sessions need you, read tool calls and diffs, and approve
or deny requests. Phone pairing and an optional encrypted relay support access
without requiring Tailscale.
### Reach it from anywhere

<img src="docs/media/control-plane/phone-approval.png" alt="A pending approval in the phone layout" width="300">
Phone pairing on the same Wi-Fi needs nothing extra. Away from home, use
Tailscale, a public tunnel with device pairing, or the optional end-to-end
encrypted relay.

Start work from a claude.ai or supported ChatGPT connector, too: send a design
or attachment into a session on your machine. Chat connectors cannot approve
agent actions.

[Terminal client](docs/terminal-client.md) · [Phone supervision](docs/mobile-sessions.md) · [Remote access](docs/remote-access.md) · [Chat connectors](docs/use-from-chat.md)
[Remote access](docs/remote-access.md) · [Relay](docs/relay.md) · [Chat connectors](docs/use-from-chat.md)

## The everyday details are here, too
### The everyday details

- **Files travel with the work.** Open a remote PDF or file path an agent prints;
the native client opens it locally, while the web app has a built-in viewer.
Expand All @@ -115,38 +166,34 @@ agent actions.

[More screenshots and file demonstrations](docs/media/control-plane/README.md) · [Full guide](docs/guide.md)

## Install
## Other ways to install

| | |
|---|---|
| **Linux / macOS** | `curl -fsSL https://raw.githubusercontent.com/JeremiahM37/lectern/main/install.sh \| sh` |
| **Homebrew** | `brew install JeremiahM37/tap/lectern` |
| **Windows** | `irm https://raw.githubusercontent.com/JeremiahM37/lectern/main/install.ps1 \| iex` (needs [Git for Windows](https://git-scm.com/download/win)) |
| **Docker** | `docker run -d -p 127.0.0.1:9110:9110 -e LECTERN_INSECURE_LISTEN=1 -v lectern-data:/data ghcr.io/jeremiahm37/lectern:latest` |
| **Go** | `go install github.com/JeremiahM37/lectern/v2/cmd/lectern@latest` |
| **deb / rpm** | Packages are attached to each [release](https://github.com/JeremiahM37/lectern/releases/latest). |

On the machine that runs Lectern, agents need only `git` and the agent's own
CLI: Lectern keeps their terminals alive itself, on Linux, macOS and Windows
([how](docs/ptyhost.md)). Other machines reached over SSH need the `lectern`
binary installed, or `tmux` and `python3`. The web terminal is built in too;
nothing else to install.
`lectern doctor` checks everything and prints a fix next to anything that's
wrong, and `lectern update` installs a new release. You can try it with no
setup at all, using fake agents: `LECTERN_MOCK=1 lectern serve` (it listens on
127.0.0.1 only).
binary installed, or `tmux` and `python3`. The web terminal is built in.
To try it with fake agents and no setup: `LECTERN_MOCK=1 lectern serve` (it
listens on 127.0.0.1 only).

## Documentation

| | |
|---|---|
| [Getting started](docs/getting-started.md) | Install, first agent, approvals on the web and phone, review and commit |
| [Full guide](docs/guide.md) | Everything in depth: sessions, tasks, auth, phone alerts, delegated builds, local models |
| [Use from claude.ai / ChatGPT](docs/use-from-chat.md) | The chat connector: setup, what a chat can do, troubleshooting |
| [Terminal client](docs/terminal-client.md) | Dashboard keys, multi-window, `lectern claude`, send-file |
| [Agents](docs/agents.md) | Catalog, capabilities per agent, custom agents |
| [Mobile sessions](docs/mobile-sessions.md) | Chat cards, approvals, voice mode |
| [Browser](docs/browser.md) | Browser pane, Design Mode, agent browser tools, computer use |
| [Remote access](docs/remote-access.md) | Phone pairing and tunnels without Tailscale |
| [Local / Docker](docs/local.md) · [Docker](docs/docker.md) | Standalone and container setups |
| [Local runtime](docs/local.md) · [Shared server](docs/quickstart.md) · [Docker](docs/docker.md) | How the private runtime works, building from source, a server for several machines, containers |

Lectern was called AgentDeck until v2.3. Old `AGENTDECK_*` settings still work.

Expand Down
2 changes: 2 additions & 0 deletions cmd/lectern/agent_quick.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,8 @@ func checkAgentInstalled(c *console.Client, agentName string) error {
msg := fmt.Sprintf("lectern %s: %s isn't installed where Lectern runs. To use it, %s, then run lectern up once so Lectern finds it.", agentName, agentName, agentInstallHint(agentName))
if len(installed) > 0 {
msg += fmt.Sprintf("\nInstalled now: %s — for example: lectern %s", strings.Join(installed, ", "), installed[0])
} else {
msg += "\nTo see how Lectern works first, try the demo agent, which needs nothing installed: lectern demo"
}
return errors.New(msg)
}
Expand Down
Loading
Loading