Skip to main content

Develop on localhost

Appilot resolves your organization from the domain the page is served on. On your own machine there is usually no such domain, which is the whole of the localhost problem. There are two ways around it, and they give you different things.

Tier 1: a loopback hostnameTier 2: bare localhost
What you run onmyapp.lvh.me:3000, or a hosts-file namelocalhost:3000, 127.0.0.1:3000
Widget keyNot neededRequired, wk_test_
Tenant resolved fromThe page domainThe key
Views, controls, forms, action plans, knowledgeYesNo
Domain verificationNot requiredNot applicable

Tier 1 is the one to use. It is the only one that gives the assistant your app context, so it is the only one where what you see locally is what your users will see in production.

Tier 1: a hostname that resolves to 127.0.0.1​

Serve your app on a real hostname that points at your own machine, then register that hostname as a domain of your app. The keyless path resolves the tenant from the page domain exactly as it does in production, and the turn gets the full content model.

Pick a hostname​

lvh.me and every subdomain of it resolve to 127.0.0.1 in public DNS, so nothing needs installing:

http://myapp.lvh.me:3000

If you would rather not depend on a public zone, add your own name to /etc/hosts (C:\Windows\System32\drivers\etc\hosts on Windows):

127.0.0.1  myapp.local

Then serve your dev server on that name. With Vite, add it to server.allowedHosts (or run vite --host); most other dev servers accept the hostname without configuration.

Register it​

In the Backoffice, open your app and add the hostname as a domain. Register the hostname alone: the port is not part of a domain, and Appilot drops it when it matches. From an MCP client, create_app takes the same hostname in domains.

You do not have to verify it. Verification gates the live widget key and the site adapter, and the keyless path needs neither. See Verify your domain.

Boot the widget with no key​

<script type="module" src="https://cdn.appilot.space/widget/v1/appilot.esm.js"></script>
<script nomodule src="https://cdn.appilot.space/widget/v1/appilot.js"></script>

The widget still needs an identified user, so run your identity relay locally too. See The identity relay.

What you get​

Everything. Views detect, controls resolve, forms and action plans load, the knowledge base answers, and a client action you registered on the page is collected on every turn. The extension behaves the same way on that hostname: see The extension for your app.

Tier 2: bare localhost with a test key​

Where the hostname trick is impossible (a fixed local certificate, a corporate proxy, an environment you do not control), create a test key and pass it to the widget.

  1. In the Backoffice, under Widget Keys, create a key and mark it a test key. Its allowed domains default to localhost and 127.0.0.1.
  2. Pass it to the widget:
<script type="module" src="https://cdn.appilot.space/widget/v1/appilot.esm.js"
data-api-key="wk_test_..."></script>
<script nomodule src="https://cdn.appilot.space/widget/v1/appilot.js"
data-api-key="wk_test_..."></script>

The Appilot cloud accepts a wk_test_ key when every entry in its allowed domains is a loopback address (localhost, 127.0.0.1, [::1], or a *.localhost subdomain). A test key with a public hostname in that list is refused in production with WIDGET_TEST_KEY_IN_PROD, so a development key cannot quietly become a production one.

What you do not get: app context. No domain resolves, so the turn carries your tenant and your user and nothing about your app. The assistant answers from the page it can read plus organization-wide knowledge; your views, controls, forms and action plans stay out of the turn. When a locally configured control does not appear to exist, this is almost always why. Move to tier 1.

Things that are not localhost domains​

localhost, 127.0.0.1, ::1 and any host:port form are refused as domain rows. A domain is a hostname, and one that means "this machine" means a different machine for every member of your organization. The refusal names the tier 1 hostname to use instead.

Local backend​

Pointing the widget at a backend you run yourself is a separate switch from either tier:

bootAppilotWidget({
widgetScriptUrl: 'https://cdn.appilot.space/widget/v1/appilot.esm.js',
appilotApiUrl: 'http://localhost:6001',
});

Without appilotApiUrl the widget bundle talks to the host it was built for, which for the CDN bundle is the Appilot cloud.