> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sesameterminal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Runbook: local development setup on Windows

# Runbook: local development setup on Windows

This runbook sets up sesame-bun on a Windows machine, runs it, and covers common
problems. The short version is in the [README](../../README.md#for-developers).

Commands are given for PowerShell unless marked Git Bash. Run them from the repository
root.

## 1. Install the prerequisites

| Tool | Version | Install |
| - | - | - |
| Git for Windows | Current | `winget install --id Git.Git` |
| Go | 1.26 | `winget install --id GoLang.Go` |
| Node.js with npm | 24 | `winget install --id OpenJS.NodeJS.LTS` |
| Task | 3.x | `winget install --id Task.Task` |

The `OpenJS.NodeJS.LTS` package follows the current LTS line. If it installs a version
other than 24, install Node 24 from [https://nodejs.org](https://nodejs.org) instead.

Close and reopen the terminal so the new `PATH` applies, then check:

```powershell theme={null}
git --version
go version        # go1.26.x
node --version    # v24.x
npm --version
task --version
```

Git for Windows also installs Git Bash and `openssl`, which the Git Bash commands below
use.

## 2. Install the dev tools and prepare the repo

```powershell theme={null}
task tools
task setup
```

`task tools` installs the dev tools the other tasks call: linters, formatters and code
generators. `task setup` prepares a fresh clone: dependencies and git hooks. Both are
defined in `Taskfile.yml`; read it for the exact steps and versions.

Go tools installed with `go install` go to the `bin` folder under `go env GOPATH`
(by default `%USERPROFILE%\go\bin`). That folder must be on `PATH`. Check with:

```powershell theme={null}
go env GOPATH
$env:Path -split ';' | Select-String 'go\\bin'
```

Run `task tools` again after pulling a change to the tool versions.

## 3. Create `.env`

```powershell theme={null}
Copy-Item .env.example .env
```

`.env` is ignored by git. Never commit it and never paste its contents into an issue,
PR or log.

Task loads the root `.env` when it runs a task. The backend reads only environment
variables and does not open `.env` itself. If you run the backend binary directly, set
the variables in the shell first.

### Required variables

**`UI_PASSWORD_HASH`**: the argon2id hash of the password you log in with.

```powershell theme={null}
task hash-password
```

Follow the command's prompt. It prints a PHC string that starts with `$argon2id$`. Put
it in `.env` in single quotes:

```dotenv theme={null}
UI_PASSWORD_HASH='$argon2id$v=19$...'
```

The quotes matter. Task's dotenv loader expands `$name` in unquoted and double-quoted
values, which would remove parts of the hash. Single-quoted values are read as they are.

**`SESSION_SECRET`**: at least 32 bytes, hex or base64 encoded. To generate 32 random
bytes as hex:

PowerShell:

```powershell theme={null}
$b = New-Object byte[] 32; [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b); -join ($b | ForEach-Object { $_.ToString('x2') })
```

Git Bash:

```bash theme={null}
openssl rand -hex 32
```

Both print 64 hex characters. Paste the output as the value.

### Optional variables

| Variable | Purpose |
| - | - |
| `HTTP_ADDR` | Address the backend listens on. Development uses `127.0.0.1:8080` |
| `DATA_DIR` | Directory for the SQLite database. Development uses `data/` |
| `COOKIE_SECURE` | Adds the `Secure` flag to the session cookie. Leave it off for `http://localhost`; turn it on behind TLS |
| `LOG_LEVEL` | Log level |
| `LOG_FORMAT` | Log output format |
| `LIVE_TRADING` | Only the exact string `true` enables trading. Leave it `false` |

`.env.example` has the defaults and accepted values for each variable. The backend
refuses to start while `GEOBLOCK_URL`, `POLYMARKET_PRIVATE_KEY`, or any
`POLYMARKET_RELAYER_*` or `POLYMARKET_BRIDGE_URL` variable is set; delete them from an
older `.env`.

Leave `POLYMARKET_SESSION_KEY` empty on this development server. It is in a blocked
location, so the Session Key has no use here (security finding F8). The public
`POLYMARKET_WALLET_ADDRESS` alone is enough for positions, activity and P\&L; open orders
and cash then report `credentials_unavailable`. Otherwise use `task dev:fake` for account
screens. Setting up real credentials on a host in a permitted location is covered in
[session-key-setup.md](session-key-setup.md).

## 4. First run

```powershell theme={null}
task dev
```

This starts two processes:

* the Go backend on `127.0.0.1:8080`, serving the REST API under `/api` and the
  WebSocket at `/ws`;
* the Vite dev server on `localhost:5173`, which serves the frontend and proxies `/api`
  and `/ws` to the backend.

Open [http://localhost:5173](http://localhost:5173) and log in with the password you hashed. Use `localhost`,
not `127.0.0.1` or the machine's IP address: the Vite dev server binds to `localhost`
only.

Check that the backend is up:

```powershell theme={null}
Invoke-RestMethod http://127.0.0.1:8080/api/health | ConvertTo-Json
```

In Git Bash: `curl -s http://127.0.0.1:8080/api/health`. In PowerShell 5.1, `curl` is
an alias for `Invoke-WebRequest`; use `curl.exe` for the real curl.

The public health check returns only `{"status": "ok"}` or `{"status": "degraded"}`.
The version, trading state, geoblock result and feed health are on `GET /api/status`,
which needs a session
([ADR 0008](../adr/0008-public-health-authenticated-status.md)). After logging in, open
[http://localhost:5173/api/status](http://localhost:5173/api/status) in the same browser, or look at the status indicator
in the app.

On a machine in a location that Polymarket blocks, `/api/status` shows
`"trading_enabled": false` and `"blocked": true`, and the startup log records the
geoblock status. This is expected; see
[ADR 0005](../adr/0005-no-geoblock-workarounds.md). Read-only work is unaffected.

Stop `task dev` with Ctrl+C.

### Run on a fake venue

For UI work that does not need Polymarket, start the app with:

```powershell theme={null}
task dev:fake
```

It runs the same two processes, with the backend started with `SESAME_FAKE_VENUE=1` so
it takes market data from a fake venue instead of Polymarket. Use it for UI work only;
check anything that depends on real venue behaviour with `task dev`.

## 5. Everyday commands

| Command | When to run it |
| - | - |
| `task dev` | Run the app |
| `task dev:fake` | Run the app on a fake venue, for UI work without Polymarket |
| `task gen` | After changing `api/openapi.yaml`, or after pulling a change to it |
| `task lint` | Before every PR; it must pass |
| `task fmt` | Before committing |
| `task test` | Before every PR |
| `task build` | To check the production build |

## 6. Regenerate code from the API spec

`api/openapi.yaml` is the source of truth. The generated code lives in:

* `backend/internal/httpapi/gen` (Go, `oapi-codegen`)
* `frontend/src/lib/api/gen` (TypeScript, `openapi-typescript`)

To change the API, edit the spec and run:

```powershell theme={null}
task gen
```

Never edit the generated folders by hand; `task gen` overwrites them. If the Go or
TypeScript build reports a type that does not match the spec after a pull, run
`task gen`.

## 7. Reset the local database

The SQLite database lives in `data/` (or `DATA_DIR`). Deleting it removes settings,
login sessions, the watchlist, layouts, the order audit log and cached market data.
The migrations recreate an empty database on the next start.

1. Stop `task dev`.
2. Delete the folder:

   ```powershell theme={null}
   Remove-Item -Recurse -Force data
   ```

   Git Bash: `rm -rf data`
3. Run `task dev` again and log in.

Keep a copy of the folder first if you need the order audit log.

## 8. Troubleshooting

### A port is already in use

The backend needs port 8080 and Vite needs port 5173. Find the process holding a port:

```powershell theme={null}
Get-NetTCPConnection -LocalPort 8080 -State Listen | ForEach-Object { Get-Process -Id $_.OwningProcess }
```

Or: `netstat -ano | findstr :8080`, then look up the PID in Task Manager.

Usually it is an earlier `task dev` that did not exit. Stop it:

```powershell theme={null}
Stop-Process -Id <pid>
```

If another program needs 8080, set `HTTP_ADDR` to another port. The Vite proxy target is
configured in the frontend; check `frontend/vite.config.ts` so that it points at the same
port.

### The backend exits at startup with a missing or invalid variable

The backend validates its configuration at startup and exits when a required variable
is missing or invalid. Check that:

* `.env` exists in the repository root (not in `backend/`);
* `UI_PASSWORD_HASH` and `SESSION_SECRET` are set and not empty;
* `SESSION_SECRET` decodes to at least 32 bytes (64 hex characters);
* you started the backend through Task, which loads `.env`. `go run` on its own does
  not.

### Login fails with the right password

* `UI_PASSWORD_HASH` is not in single quotes, so `$` parts were expanded. Quote it and
  restart `task dev`.
* `.env` was edited while `task dev` was running. Task reads `.env` only at start;
  restart it.
* Too many failed attempts. Login is rate limited and returns HTTP 429; wait and try
  again.

### Requests fail with 403 after login

Mutating requests need the session cookie and a CSRF token. If the session has expired,
reload the page and log in again.

### `task` or a tool is not recognized

* `task`: reopen the terminal after installing, or check `winget list --id Task.Task`.
* `golangci-lint`, `gofumpt`, `oapi-codegen` and other Go tools: add the Go `bin` folder
  to `PATH` (section 2) and reopen the terminal.

### Trading shows as disabled

Log in, then check `GET /api/status` (open [http://localhost:5173/api/status](http://localhost:5173/api/status) in the
same browser):

* `geoblock.blocked: true`: the location is blocked, or the check has not succeeded yet
  (`geoblock.error` then holds the reason). Trading stays off. The app has no setting
  that changes this.
* `LIVE_TRADING` is anything other than the exact string `true`.

### Files show as changed right after checkout

`.gitattributes` checks text out with LF line endings. If your editor saves CRLF, set it
to use LF for this repository. Files under `testdata/` are kept byte for byte; do not
reformat or re-save them.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.