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 hostname | Tier 2: bare localhost | |
|---|---|---|
| What you run on | myapp.lvh.me:3000, or a hosts-file name | localhost:3000, 127.0.0.1:3000 |
| Widget key | Not needed | Required, wk_test_ |
| Tenant resolved from | The page domain | The key |
| Views, controls, forms, action plans, knowledge | Yes | No |
| Domain verification | Not required | Not 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.
- In the Backoffice, under Widget Keys, create a key and mark it a test
key. Its allowed domains default to
localhostand127.0.0.1. - 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.
Related
- The identity relay: the local half every tier needs.
- Widget installation: the full boot reference.
- Client actions: making your page's own capabilities callable.
- End to end with the widget: the whole path, in order.