Lantern
Lantern lets a Next.js app use the Codex or Claude account already signed in on each user's Mac.
The user installs Lantern, approves your site, and keeps Lantern running while they use your app. Your AI SDK route sends supported requests to their Mac, where Lantern starts the signed-in CLI. The CLI calls its online service and streams the answer back to your interface.
Your app does not collect the user's provider login or model API key. Requests use the user's existing account and its normal limits. Prompts, attached images, and replies still pass through your app, Lantern's relay, and the provider.
Lantern is a good fit for interactive features where the user and their Mac are present. It is not an always-on backend for scheduled or unattended work.
Add Lantern to your app
The integration has three pieces: a server helper, a connection route, and a Connect Lantern button. Your existing AI route keeps its prompt and interface but uses Lantern's provider for the request. The user supplies the account and must keep their Mac awake, Lantern running, and the CLI used for the request signed in.
To let a coding agent add the integration for you, install Lantern's open Agent Skill:
npx skills add sunbeam-za/lantern --skill add-lantern
Then ask your agent: Add Lantern to this Next.js app. Reuse its existing AI route and put the connection control next to the feature. The skill works with Agent Skills-compatible tools including Codex, Claude Code, Cursor, and GitHub Copilot.
Install Lantern and the Vercel AI SDK:
npm install @sunbeam-za/lantern ai @ai-sdk/react
Generate a stable server secret:
openssl rand -base64 32
Save the output in your app's environment:
# .env.local
LANTERN_APP_SECRET=paste-the-generated-value-here
Create one server-only helper:
// lib/lantern.ts
import { createLanternApp } from "@sunbeam-za/lantern/next";
export const lantern = createLanternApp({
appName: "Tiny Weather",
secret: process.env.LANTERN_APP_SECRET!,
});
Expose its connection route:
// app/api/lantern/route.ts
import { lantern } from "@/lib/lantern";
export const { GET, POST, DELETE } = lantern;
Render the connect button:
import { LanternConnect, LanternProvider } from "@sunbeam-za/lantern/react";
<LanternProvider>
<LanternConnect appName="Tiny Weather" />
{children}
</LanternProvider>
On a public marketing page, you can show the integration without setting up a provider or connection state:
import {
WorksWithLanternBadge,
WorksWithLanternSection,
} from "@sunbeam-za/lantern/react";
<WorksWithLanternBadge />
<WorksWithLanternSection tone="dark" />
Both components include portable inline styles. Use className and style for the surrounding layout, replace the section copy and actions through props, or set href={null} on the badge when it should not link to Lantern.
Setup can make Codex, Claude, or both available. The button uses text support by default, chooses preferredModel when that CLI is available, and otherwise uses the first compatible CLI Lantern reports.
Then use Lantern in your AI SDK route:
// app/api/chat/route.ts
import { streamText } from "ai";
import { lantern } from "@/lib/lantern";
export async function POST(request: Request) {
const local = lantern.server(request);
const { prompt } = await request.json();
return streamText({
model: local.provider(local.model),
prompt,
}).toTextStreamResponse();
}
lantern.server(request) reads the browser's signed connection cookie and returns a Vercel AI SDK provider for the Lantern that user approved.
Supported today: text, streaming text, cancellation, and Codex-backed image generation and edits. Client-defined tools and structured response formats are not supported.
See the Vercel AI SDK guide for text, streaming, and image examples.
What each user does
Each user needs macOS 14+, Node.js 22+, and a signed-in Codex or Claude CLI. The Mac app guides them through setup, or they can run:
npx @sunbeam-za/lantern setup
Setup finds Codex and Claude, saves the user's choices, and asks whether to start Lantern.
When your app renders Connect Lantern, Lantern shows the app name and exact site address before the user approves it. Approval allows future supported requests from that address while Lantern is running and online; it is not a new prompt for every request.
Start Lantern again later with:
npx @sunbeam-za/lantern start
How a request travels
- The user approves your app in Lantern on their Mac.
- Your app stores the selected model and relay destination in an HttpOnly cookie signed by your server.
lantern.server(request)sends each AI SDK call to the Mac stored in that cookie, where Lantern verifies the app and starts Codex or Claude.- The reply comes back over the same path.
lantern.hiltonbarber.com is the relay. It uses the browser's saved connection to route each request to the user's Lantern and returns the response along the same path. Provider credentials stay in the CLI on the user's Mac; prompts, attached images, and responses pass through your app, the relay, and the provider service.
The browser keeps this connection for up to 30 days by default. Deleting it in one browser does not revoke the site's approval on the Mac. Stopping Lantern pauses every approved site. Running setup again and restarting Lantern clears all approvals.
Pairing and tunnel identity
The app has one Ed25519 signing identity derived from LANTERN_APP_SECRET. The user approves its public key for the app's exact origin.
Lantern on the Mac has a separate persisted Ed25519 identity in ~/.lantern/tunnel.json. It uses that identity when opening its outbound WebSocket. Pairing returns an opaque relay ticket for this identity, and your app places the ticket inside its HMAC-signed HttpOnly session.
The server provider gets its destination from that authenticated session. Request input contains the AI operation and model rather than a tunnel URL. Each execution opens a short-lived app-side WebSocket to the relay; only the local Lantern keeps a persistent outbound connection. The relay joins those two connections by ticket, while the local gateway—not the relay—verifies the app's signed execution envelope.
Supported CLIs
Lantern starts signed-in CLIs on the user's computer:
- Codex App Server through
codex app-server --stdio - Codex CLI through
codex exec --json - Claude CLI through
claude -p --output-format stream-json
Setup publishes one or both CLI connections as model names such as codex and claude. Lantern normalizes their process and event formats behind the provider used by your app.
Standard app sessions run in fresh temporary directories. Codex uses its read-only sandbox, while Claude starts in safe mode with an empty tool list. The CLIs still call their normal online services; Lantern does not run either model offline.
Lower-level interfaces
The server-side Vercel AI SDK provider is the main web-app interface. Lantern also exposes APIs for tools on the same Mac:
- OpenAI-compatible
GET /v1/modelsandPOST /v1/chat/completions - Anthropic-compatible
POST /v1/messages - OpenAI-compatible
POST /v1/images/generationsandPOST /v1/images/edits - Streaming multi-turn sessions at
WS /v1/socket GET /v1/catalogfor providers, model names, and availabilityGET /healthto check whether Lantern is running
Local scripts authenticate with the token in ~/.lantern/token. See the REST and WebSocket reference.
Configuration
Guided setup writes the local configuration and credentials with owner-only file permissions:
~/.lantern/config.json: Codex and Claude connections, approved apps, and session settings~/.lantern/token: local REST and WebSocket credential~/.lantern/tunnel.json: stable outbound tunnel identity
The committed sunbeam.config.example.json documents the service and provider settings.
Develop
git clone https://github.com/sunbeam-za/lantern.git
cd lantern
npm install
cp sunbeam.config.example.json sunbeam.config.json
export SUNBEAM_TOKEN="$(openssl rand -base64 32)"
npm run dev
The hosted relay needs LANTERN_RELAY_SECRET and REDIS_URL. Local Lantern processes can point at a development relay with LANTERN_RELAY_URL.
npm test
npm run typecheck
npm run build
npm run site:dev
npm run site:build
npm run check runs backend types, tests, the gateway build, and the Next.js production build.