Skip to content

OpenWalkHQ/openwalk

Repository files navigation

OpenWalk

An in-app assistant for your own website, embedded with one script tag. Users ask in plain language; OpenWalk answers right on the page.

What it does

Walks users through tasks. Ask "how do I invite a teammate?" and OpenWalk highlights the one next element to click, explains why, and replans after every action against the live DOM – guidance never goes stale. It gets better with use: the server passively learns which screens exist and how they connect (element labels and paths only, never anything anyone types), so it can route people to a setting buried three screens deep.

Explains the screen. Ask "what is this?" or "which plan am I on?" and the vision route reads the current page – visible text and form state, ephemeral, nothing stored – then answers with notes pinned to the exact fields and sections it talks about, one at a time.

Helps fill forms. On a confusing form, OpenWalk explains each field and proposes values; clicking a note's Insert button places the suggestion into the field. The user stays in charge: insertion only ever happens on that explicit click, never into password fields, and nothing is ever submitted – the widget highlights and narrates, users do every click.

Bring your own LLM key (Google, Anthropic, or OpenAI), point it at any Postgres, and it is yours: no gateway, no telemetry, no lock-in. MIT licensed.

Want this on other people's sites instead of your own? That's openwalk.ai – a hosted service with a browser extension, built on this exact code.

Quick start

Requires Node ≥ 24 and Docker.

  1. Run the server:

    npm install
    cp .env.example .env      # fill in the three required lines, see below
    docker compose up -d db   # local Postgres on :54320
    npm start

    .env needs a provider key (GOOGLE_GENERATIVE_AI_API_KEY for the default model), an OPENWALK_ADMIN_TOKEN (any secret, e.g. openssl rand -hex 24), and OPENWALK_ALLOWED_DOMAINS – the sites this server is willing to serve. The server refuses to start without the last two; an accidentally open deployment is not a thing.

  2. Embed the widget in the app you allowed:

    <script src="http://localhost:3900/widget.js" defer></script>

    The widget appears bottom-right; ask it something like "how do I invite a teammate?".

  3. Inspect your data: open http://localhost:3900 for the admin UI – see what has been collected per site, watch routing decisions and token spend, and purge anything.

Self-hosting with Docker

GOOGLE_GENERATIVE_AI_API_KEY=... OPENWALK_ADMIN_TOKEN=... \
OPENWALK_ALLOWED_DOMAINS=yourapp.com docker compose up

One command brings up the server and its Postgres (compose also reads a ./.env file, which beats inlining secrets). To use an external Postgres, run the image directly:

docker build -t openwalk .
docker run -p 3900:3900 \
  -e DATABASE_URL=postgres://… \
  -e GOOGLE_GENERATIVE_AI_API_KEY=... \
  -e OPENWALK_ADMIN_TOKEN=... \
  -e OPENWALK_ALLOWED_DOMAINS=yourapp.com \
  openwalk

The server is a single plain-Node process; it runs anywhere Node 24 and a Postgres connection exist — Fly.io with Neon Postgres is a proven combination.

Embedding

Say you run yourapp.com and deploy OpenWalk at openwalk.yourapp.com:

  1. Deploy the server with OPENWALK_ALLOWED_DOMAINS=yourapp.com. It will plan, store, and answer CORS for your product's pages and refuse everything else – nobody can quietly point your instance (and your LLM bill) at other sites.

  2. Add the tag to your app's pages:

    <script
      src="https://openwalk.yourapp.com/widget.js"
      defer
    ></script>

    The widget talks to the origin the script came from (data-server overrides it). Privacy rules are built in – element labels only, never typed values – and chat state lives in the page's own sessionStorage.

Who builds the site knowledge

By default every widget user's browsing feeds the site knowledge (pages and navigation edges – labels and structure only). You may not want your users contributing anything stored: seed the knowledge yourself by clicking through the app with the widget once, then turn off Collect site knowledge for the origin in the admin UI (Sites → your site). The server then drops /observe payloads for that origin – guidance keeps using the knowledge you seeded, it just stops growing. Add data-observe="off" to the script tag so your users' widgets don't even send observations; the admin switch stays the actual guarantee, enforced server-side.

Security model

Plainly, so you can decide what to rely on:

  • OPENWALK_ADMIN_TOKEN (required). Bearer token for the admin API (/api/*): everything collected, and the power to purge it. The server refuses to boot without one.
  • OPENWALK_ALLOWED_DOMAINS (required). The widget API (/guide, /observe) only serves pages on these domains, enforced server-side on the page URL of every request – it bounds what your deployment will ever plan for or store, no matter who calls it. It also drives CORS: only allowlisted origins get cross-origin browser access, so other websites can't use their visitors' browsers against your server. (* exists for personal any-site servers and is always an explicit choice.)
  • OPENWALK_TOKEN (optional). Shared Bearer token on the widget API. Real protection for a private server; on an embed it sits in your page source, so treat it as an app id, not a secret.
  • API keys never reach the browser. The LLM provider key lives in server env only. The widget talks to your server, nothing else.
  • What the server stores is deliberately boring: page digests (path, title, element labels), navigation edges, walkthrough recipes (goal + instructions), routing traces (goal keywords only), and token usage counts. Values typed into the page are never stored. Explain questions send the visible page text and field values (passwords masked in the browser) for the answer only – rendered into the prompt, persisted nowhere.
  • OPENWALK_BLOCKED_DOMAINS (optional). Subtracts from the allowlist – a kill switch that always wins.

One honest caveat: an allowlisted deployment still exposes /guide to anonymous callers claiming to be on your domain (they can burn your LLM tokens, they just can't do anything else). If that risk matters to you, put a rate limiter or your CDN's bot protection in front. The composable server (createOpenwalkHandler – auth, model resolution, and usage metering are injectable) is how openwalk.ai adds per-user authentication on top.

Configuration

Env var Default Purpose
DATABASE_URL required postgres://… storage connection
OPENWALK_ADMIN_TOKEN required Bearer token for the admin API (/api/*)
OPENWALK_ALLOWED_DOMAINS required comma-separated domains the widget API serves (subdomains included); * = any
GOOGLE_GENERATIVE_AI_API_KEY key for google/… models (the default model is Gemini)
ANTHROPIC_API_KEY key for anthropic/… models
OPENAI_API_KEY key for openai/… models
OPENWALK_MODEL google/gemini-2.5-flash provider/model for the planner and router
OPENWALK_VISION_MODEL OPENWALK_MODEL optional smarter model for explain answers (planning stays on the fast model)
OPENWALK_TOKEN (off) optional Bearer token on the widget API
OPENWALK_BLOCKED_DOMAINS (none) domains to refuse even if allowed (kill switch)
PORT 3900 server port

How it works

  1. On pages carrying the widget, it passively records a digest of each screen a user visits – path, title, element labels – one row per page (site knowledge).
  2. When someone asks something fresh, a tiny router call decides between two paths: explain – the question is about the current screen, so the vision model reads the page's visible text and answers, optionally pinning note bubbles to the fields it explains – or navigate – the step planner takes over.
  3. For walkthroughs, the LLM picks the one element on the current page to act on next – site knowledge steers navigation, but only live elements are ever highlighted.
  4. The widget outlines it with an instruction bubble; clicking the element (or pressing Next) advances, closing the bubble stops the walkthrough.
  5. After each action – including full page navigations – the page is rescanned and the next step is planned from what is actually on screen.
  6. Every routing decision (route, reason, model, goal keywords) is recorded and shown in the admin dashboard's Routing area, and every LLM call's exact token usage in its Usage area.

Development

npm run dev:server     # node --watch, runs the TypeScript directly
npm run dev:admin      # vite dev server for the admin UI, proxies /api
npm run build          # server + admin + embeddable widget
npm run typecheck
npm test               # unit tests (db suite skipped without TEST_DATABASE_URL)
npm run test:pg        # full suite against a throwaway Dockerized Postgres

Layout: server/ (plain-Node API + composable handler), widget/ (the in-page widget, built into admin/dist/widget.js), admin/ (React admin UI, served by the server at /).

Roadmap

  • Shared community walkthrough library (opt-in publishing, versioned per app)
  • Knowledge export/import between origins (seed production from staging)

License

MIT.

Guided automation of a third-party app may conflict with that app's terms of service. OpenWalk only ever highlights and explains – users do the clicking – but use it on apps you're authorised to use.

About

Ask 'how do I…?' on any web app and get walked through it step by step, right on the page. Self-hosted, BYOK, MIT.

Topics

Resources

License

Stars

2 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages