<!-- Source: https://docs.squirro.com/en/latest/technical/neo/dev-guide/local-development.html -->
# Local Development

> **Warning**
>
> Project Neo is currently in [Technical Preview](../../../a-z/squirro-glossary.md#term-Technical-Preview). Features described in this section may change before general availability.

This page explains how the development environment works, covering the two-server architecture, live reload, environment variables, and common workflows.

## How the Dev Server Works

Running `neo-ui dev` starts two processes in parallel.

### Dev Proxy (Port 5555)

The proxy is a lightweight Node.js server that does three things:

1. **Serves the pre-built host application**

   The core Project Neo application (chat, search, collections) is already bundled inside `@squirro/neo-core`. The proxy serves it as static files.

   > **Note**
   >
   > The host application version is tied to your installed version of `@squirro/neo-core`. To avoid unexpected behavior, make sure that version matches the Project Neo version running on your target Squirro instance. Run `npm list @squirro/neo-core` to check your installed version.
2. **Mocks authentication**

   You do not need to log in. The proxy always reports the session as authenticated and injects your refresh token (from `.env`) into outgoing API requests as a valid access token. That token carries the full permissions of the account it belongs to. To test permission-restricted scenarios, use a real Squirro instance with an account that has the appropriate role.
3. **Proxies Squirro API calls**

   Requests to `/api/*`, `/v0/*`, `/service/*`, `/studio/*`, and `/storage/*` are forwarded to your Squirro instance with authentication headers injected automatically.

> **Warning**
>
> The dev proxy bypasses authentication entirely. Do not expose port 5555 to untrusted networks. Run it on localhost only. If you are working in a cloud development environment or on shared Wi-Fi, make sure the port is not publicly reachable.

### Bundle Dev Server (Port 3001)

Rsbuild compiles your bundle code and serves it via Module Federation. The host loads your bundle manifest from `http://localhost:3001/mf-manifest.json` and your dashboard components from the chunks served on port 3001.

## Live Reload

The dev proxy watches your `src/` directory. When any file changes:

1. The proxy detects the change within approximately 300ms.
2. It sends a reload signal to the browser via a Server-Sent Events connection.
3. The browser performs a full page reload.
4. The host fetches the latest bundle chunks from the dev server.

Each change triggers a full page reload, not hot module replacement. That choice is intentional. Module Federation and HMR do not combine reliably. Reloads are fast because the host application is pre-built and served from a local file cache.

## Environment Variables

All configuration lives in `.env` at the project root. Edit it with `neo-ui config` or open the file directly.

| Variable | Default | Required | Description |
| --- | --- | --- | --- |
| `SQUIRRO_API_URL` | None | Yes | Your Squirro instance URL, for example `https://your-instance.example.com`. |
| `SQUIRRO_TOKEN` | None | Yes | Refresh token from your Squirro profile. |
| `BUNDLE_PORT` | `3001` | No | Port for the bundle dev server. |
| `DEV_PROXY_PORT` | `5555` | No | Port for the dev proxy. Open this port in your browser. |
| `VITE_PROJECT_ID` | `demo-project` | No | Default project ID used by the standalone preview at port 3001. It is not used by the full host at port 5555, which derives the project ID from the URL. |
| `NEO_UI_INSECURE_TLS` | None | No | Set to `1` to skip certificate verification. It applies only to an instance on a loopback or private-network host, and is ignored, with a warning, for any other host. |
| `NEO_UI_ALLOW_INSECURE_HTTP` | None | No | Set to `1` to allow a plain `http://` value in `SQUIRRO_API_URL` for a public host. The refresh token then travels in clear text, so use it for isolated test instances only. |

### Instances With a Private CA or a Self-Signed Certificate

The dev proxy verifies the instance TLS certificate the same way your browser does. If your instance uses a certificate issued by a company CA, point Node at the CA bundle rather than turning verification off. Node reads `NODE_EXTRA_CA_CERTS` when the process starts, so set it in the shell, or export it in your shell profile, and not in `.env`, which is loaded too late to take effect:

```bash
NODE_EXTRA_CA_CERTS=/path/to/company-ca.pem neo-ui dev
```

For a throwaway instance on your own machine or local network, such as `localhost`, a `.local` name, or an address in the `10.x` or `192.168.x` ranges, `NEO_UI_INSECURE_TLS=1` skips verification. It has no effect on a public host: the refresh token in `.env` is a long-lived credential and must never cross an unverified connection to the internet.

Plain `http://` instance URLs are accepted for those same private hosts. For any other host, the CLI refuses the URL unless `NEO_UI_ALLOW_INSECURE_HTTP=1` is set.

### Updating Credentials

```bash
neo-ui config
```

Select API credentials from the menu. The CLI validates the new token before saving.

If your token expires during a development session, `neo-ui dev` begins returning 401 errors from the API. Run `neo-ui config` to refresh the token, then restart the dev server.

## Checking Your Bundle Is Running

Open `http://localhost:3001/mf-manifest.json` in a browser tab. If you see JSON like this, the bundle server is running correctly:

```json
{
  "id": "my_dashboards",
  "name": "my_dashboards",
  "exposes": {
    "./manifest": "./src/manifest.ts"
  }
}
```

If that URL returns an error or times out, the bundle dev server is not running. Restart `neo-ui dev`.

## Running the Dev Server for a Specific Project

By default, the standalone preview at port 3001 uses a placeholder project ID. To develop against a real Squirro project, set `VITE_PROJECT_ID` in `.env`:

```bash
VITE_PROJECT_ID=your-actual-project-id
```

This value is used only by the standalone preview at port 3001. The full host at port 5555 derives the project ID from the URL and ignores `VITE_PROJECT_ID`. The project ID is passed to your dashboard components as the `projectId` prop, and your API calls target the real project.

### Finding Your Project ID

The project ID appears in the URL after `/neo/` when you navigate to any page inside a project in Project Neo. Open your Squirro instance in a browser, select a project, and read the ID from the address bar:

```text
https://your-instance.example.com/neo/<project-id>/chat
                                      ^^^^^^^^^^^^
```

Copy that value and paste it into `VITE_PROJECT_ID` in your `.env` file.

## Port Conflicts

If a default port, or a port set in `.env`, is already in use when you run `neo-ui dev`, the command falls back to the next free port for that run. It prints a notice and leaves `.env` unchanged, so no action is required.

A port you request explicitly with `--port` or `--proxy-port` is never moved. If it is busy, the command exits with an error:

```text
✗ Bundle port 3001 is already in use.
  Free the port, pass a different one, or set BUNDLE_PORT in .env
```

To find the process using a port (macOS and Linux):

```bash
lsof -i :3001
```

To stop it if safe to do so:

```bash
kill $(lsof -ti :3001)
```

To change the port in `.env` instead:

```bash
neo-ui config
```

## Running Multiple Bundles

Each project uses two ports. To run two bundles at the same time:

1. Change `BUNDLE_PORT` and `DEV_PROXY_PORT` to unused port numbers in the second project `.env`.
2. Run `neo-ui dev` in both project directories.

Each dev proxy serves an independent host instance, so you can work on multiple bundles without conflicts.

## Typical Development Workflow

```bash
# 1. Start everything
neo-ui dev

# 2. In a second terminal, add a dashboard
neo-ui create dashboard MyNewDashboard

# 3. Open http://localhost:5555 in your browser
#    Navigate to the new dashboard in the sidebar

# 4. Edit src/dashboards/MyNewDashboard.tsx and save
#    The browser reloads to show your changes

# 5. Check types
npx tsc --noEmit

# 6. Lint and format
npm run lint
npm run format
```

## Debugging

### Source Maps

`neo-ui dev` generates source maps automatically. When an error occurs in your dashboard, the browser DevTools Sources panel and stack traces point to the original TypeScript file and line number, not the compiled output. Source maps are not generated by `neo-ui build`, so production bundles show minified code only.

### React DevTools

The [React DevTools browser extension](https://react.dev/learn/react-developer-tools) works with bundle dashboards. Bundles share the host React instance, so the component tree in DevTools includes both host components and your dashboard components together.

Name your components explicitly so they appear with readable labels:

```jsx
// Avoid: shows as "Anonymous" in DevTools
export default ({ projectId }: Props) => <div>...</div>;

// Prefer: shows as "Analytics" in DevTools
export default function Analytics({ projectId }: Props) {
  return <div>...</div>;
}
```

### Network Tab

To inspect which bundle files the host loaded, open the DevTools Network tab while running `neo-ui dev`:

- Filter by `3001` to isolate bundle requests.
- On first page load, look for `mf-manifest.json`. A 200 response confirms the bundle server is reachable.
- On first navigation to a dashboard, look for a `[hash].js` request from `localhost:3001`. That is the dashboard chunk loading on demand.

In production, the same requests come from the Squirro backend. Filter by `ui_bundles` to find them:

```text
/v0/workspaces/<domain>/ui_bundles/<project-name>/dist/mf-manifest.json
/v0/workspaces/<domain>/ui_bundles/<project-name>/dist/[hash].js
```

A 404 on `mf-manifest.json` means the bundle was not uploaded or the project configuration does not point to the correct bundle name.

### Bundle Load Errors

When the host fails to load a bundle, it logs a structured message to the browser console with the `[extensions]` prefix:

```text
[extensions] ════════════════════════════════════════════════════════════
  ✖  BUNDLE LOAD FAILED
  /v0/workspaces/<domain>/ui_bundles/<project-name>/dist/mf-manifest.json
  ...error message...
[extensions] ════════════════════════════════════════════════════════════
```

Open the Console tab and filter by `[extensions]` to catch these without noise from other sources.

### React Query DevTools

To inspect query state, cached data, and request timing, add the React Query DevTools panel to a dashboard component. Install the package first:

```bash
npm install --save-dev @tanstack/react-query-devtools
```

Then add it to the component:

```jsx
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

export default function Analytics({ projectId }: { projectId: string }) {
  return (
    <>
      {/* your dashboard content */}
      <ReactQueryDevtools initialIsOpen={false} />
    </>
  );
}
```

The panel appears as a floating button in the bottom corner of the page. Remove the import before deploying to production.
