Navlog Example App

Navlog is the flagship B4.run example: a VFR flight planner for a Cessna 172N, with an agent server and a web client. Try it live, run it on your machine, deploy your own copy, or start a new app from it.

The demo is a learning tool, not a flight-planning service. Its weather is live, but its airport and navaid data is a dated snapshot. Never use it for real flight planning.

What does it show?Copy link to section: What does it show?

A planning run uses most of B4.run's agent features in one route. The table maps what you see to the feature and the file behind it.

What you seeB4.run featureWhere
A planning answer with a bottom line, hazards and key numbersStructured output from a client-supplied response schemaweb/app/brief/, server/b4.config.ts
Live METARs, TAFs, winds aloft and advisoriesTools that call aviationweather.gov (no key)server/src/tools/get*.ts
Weather and performance worked out in parallelSubagents with their own tool listsserver/src/app/navlog/subagents/
A to-do list as the plan runsPlanningserver/src/app/navlog/plan.md
Headings, wind correction and fuel, never guessed by the modelA tool that does the math in code from the POH tablesserver/src/tools/computeNavlog.ts, server/src/lib/
Your tail number and fuel remembered next timeMemory with reviewed candidatesserver/src/app/navlog/memory.ts
"File the plan" asks before it actsPermissions with an approval gateserver/src/app/navlog/index.ts (tools.approve)
Threads you can come back toDurable threads, owned by each visitorserver/src/auth.ts, server/src/thread-access.ts
The chat, the steps and the approval cardThe AG-UI endpoint with CopilotKit and the B4 activity kitweb/app/components/NavlogChat.tsx

Run it locallyCopy link to section: Run it locally

You need Node.js 24, pnpm, and an OpenAI API key for live runs. The unit tests and evals run without a key.

  1. 1

    Get the code

    Clone the repository and install from the root:

    bash
    git clone https://github.com/cacheplane/b4run.git
    cd b4run
    pnpm install
    pnpm build
  2. 2

    Give the server a key

    The server holds the OpenAI key. The web client never sees it:

    bash
    cd examples/navlog
    cp server/.env.example server/.env
    # edit server/.env and set OPENAI_API_KEY
  3. 3

    Start both apps

    bash
    pnpm dev

    This starts the B4.run server on port 3002 and the web client on port 3010. Open http://localhost:3010 and plan a route.

To start a new app from the example rather than work in the repository, scaffold it:

bash
npm create b4-app@latest my-navlog -- --template navlog

The scaffold is the same code as examples/navlog, kept byte for byte in sync by a test. Build a Flight Planner walks through it from there.

How is it built?Copy link to section: How is it built?

Navlog has two packages. server/ is a B4.run app, and web/ is a Next.js client that talks to it over AG-UI.

The request pathCopy link to section: The request path

A plan is one streaming request from the browser to the agent:

text
browser
  → POST /api/copilotkit/…           (web: CopilotKit runtime, the visitor guard)
  → B4HttpAgent({ responseSchema })   (web: adds the brief kit's JSON Schema)
  → POST /agui/%2Fnavlog%23agent      (server: the navlog route over AG-UI)
  → the coordinator, its subagents and tools
  ← streamed events: text, tool steps, the plan, an approval card

The client's other calls go through small, allowlisted proxies: /api/b4 for memory review and /api/waypoints for route autocomplete.

The serverCopy link to section: The server

PartWhat it does
src/app/navlog/index.tsThe coordinator route. Its prompt is the procedure: read the aircraft baseline and recall the pilot's overrides, resolve the departure time, look up the waypoints, send out the subagents, compute the navlog, and answer as a brief.
src/app/navlog/subagents/weatherFetches METARs, TAFs, winds aloft and advisories for every airport and en-route station, and returns a fixed-format brief with a GO, CAUTION or NO-GO verdict.
src/app/navlog/subagents/performanceReads the POH tables for takeoff, climb, cruise and landing figures.
src/tools/One file per tool. lookupAirport and lookupNavaid resolve waypoints. findRouteStations finds reporting stations within 25 nm of the course. computeNavlog does all the navigation arithmetic. fileFlightPlan writes the ICAO plan behind an approval.
src/lib/Pure code the tools call: great-circle geometry, the wind triangle, POH table interpolation and the ICAO flight-plan format, each with unit tests.
data/navaids.jsonThe US navaid snapshot from OurAirports (public domain), rebuilt by scripts/build-waypoints.mjs.
workspace/The read-only reference corpus the agent reads: the aircraft baseline, the 172N POH and the regulations.
b4.config.tsRead-only paths for the corpus, memory writes as reviewed candidates, and server.agui.clientForwardedProps, which lets the navlog route accept the client's responseSchema.

The web clientCopy link to section: The web client

PartWhat it does
app/components/AppShell.tsxConnects to the server, holds the thread list, and mounts one workbench per thread.
app/components/WorkbenchLayout.tsxThe layout: three docked columns on a desktop (sidebar, chat, map over the sheet), and Chat, Map and Navlog tabs on a phone.
app/components/NavlogChat.tsxCopilotKit's chat with the B4 activity kit's step views and approval card.
app/components/RouteMap.tsx, RouteBar.tsxThe Leaflet map with flight-category markers and non-overlapping labels, and the route bar with waypoint autocomplete.
app/components/NavlogSheet.tsxThe navlog sheet: Legs, Weather (origin, en route, destination), Totals and plan, and Brief.
app/brief/The brief kit: the schema the agent answers in and the React components that render it.
app/lib/Pure selectors that read each surface's data from the run's events, with unit tests.
app/api/The CopilotKit runtime route and the guarded proxies. Every route checks origin, visitor and rate limit.

The map, the sheet and the chat all read from the same run. Each surface finds its data in the tool results with a pure selector. For example, latestNavlogResult returns the most recent computeNavlog result, so a replan updates everything at once.

Deploy your ownCopy link to section: Deploy your own

The live demo runs the server on Railway and the web client on Vercel. Each package's README has the full steps. The shape of the setup:

  • Server (server/Dockerfile.railway, server/main.mjs): runs the b4 build output with Postgres for threads, checkpoints, permissions and memory. It refuses to boot in production without a shared secret.
  • Web (web/vercel.json): a Vercel project rooted at examples/navlog/web, with an 800-second limit on the CopilotKit route because a plan streams for several minutes.
VariableWherePurpose
OPENAI_API_KEYserverThe model key. Give the deployment its own key with a monthly spend cap.
DATABASE_URLserverPostgres (with pgvector) for durable threads and memory.
B4_INTERNAL_TOKENbothThe secret the web client sends on every call. At least 32 characters.
B4_SERVER_URLwebThe server's public URL.
B4_DEMO_ORIGINSwebThe site's own origins, for the origin check.
UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKENwebThe rate limiter's store.

Use it as a starting pointCopy link to section: Use it as a starting point

Most of what makes navlog a flight planner lives in four places. To adapt it, change these:

  • The aircraft and the corpus. server/workspace/aircraft/ and server/workspace/poh/ hold the airplane's data. The agent reads them through readDoc.
  • The procedure. The coordinator's prompt in server/src/app/navlog/index.ts says what to do and in what order. The subagents' prompts say how to brief weather and performance.
  • The tools. Each file in server/src/tools/ is one tool, typed from its input. Keep arithmetic in src/lib/ and out of the model.
  • The answer's shape. web/app/brief/schema.ts declares the components the agent may answer with. The server binds that schema as the model's structured output.

The tests are the safety net. pnpm test in examples/navlog runs the server and web unit tests, and pnpm --filter ./server eval replays the recorded evals. Neither needs a key.