# AGENTS.md — coding agent onboarding for {{name}}
> **You are a coding agent working in `node-app-{{name}}`.**
> This app was scaffolded by `node-app new --profile bun-fullstack`. It is a
> **platform-loaded Bun app with an embedded UI bundle** — the platform's
> `BunLoader` owns the backend lifecycle, and the React+Vite UI in `ui/`
> is mounted same-origin inside an iframe at
> `/api/v2/node-apps/{{name}}/ui/`.
## What this means for you
- **No `Bun.serve()`, no own HTTP listener.** Inbound capability invocations
arrive via Unix Socket IPC through `@econ-v1/app-sdk`'s `runNodeApp(...)`.
- **No own systemd unit.** The platform starts/stops this app.
- **Eligible for the shared Bun runtime.** When enabled per-app at install
time (`shared_runtime_enabled: true`), the platform runs this app as a
`worker_threads.Worker` inside a shared `bun-runtime-host` process instead
of its own `bun run` child. The SDK's module-globals are realm-isolated
per-Worker so this is transparent to your code.
- **UI is served by the platform, not by you.** Build with `bun --cwd ui build`,
ship `ui/dist/` via the .deb, and the platform's reverse-proxy mounts it
at `/api/v2/node-apps/{{name}}/ui/`. Same-origin → no CORS, no token
flapping.
- **Shared deps externalized at build time.** `@anthropic-ai/sdk`, `openai`,
`@google/genai`, `@mistralai/mistralai`, `zod` live in
`/usr/lib/node/shared/node_modules` and are resolved at runtime via
`NODE_PATH`. Your bundled `dist/index.js` inlines everything else.
## First files to open
| `src/index.ts` | the capability handler |
| `ui/src/App.tsx` | iframe handshake (postMessage `ready` → `auth_token`) |
| `manifest.json` | declare your `capabilities.requires` / `capabilities.provides`; `has_ui: true`, `ui_path: "ui/dist"` |
| `package.json` | add deps you need (keep `@econ-v1/app-sdk` pinned) |
| `ui/package.json` | UI deps (React 19 + Vite 6 pre-installed) |
| `node-app.toml` | per-app dev config |
## UI ↔ backend handshake
The iframe is same-origin. The handshake is:
1. UI mounts → posts `{ type: 'ready' }` to `window.parent`.
2. Platform replies with `{ type: 'auth_token', token, expires_at }`.
3. UI uses the token as `Authorization: Bearer ${token}` for any backend
fetches (`/api/v2/node-apps/{{name}}/api/...`).
4. Platform refreshes by posting another `auth_token` before expiry.
The default `ui/src/App.tsx` wires this up — copy the pattern when adding
your own routes.
## Useful commands
```bash
node-app audit # check this app against the blueprint
node-app dev # hot-reload dev loop against a local monorepo
node-app build # bundle dist/index.js + ui/dist (phase 3 of #868)
node-app package # build .deb (phase 3 of #868)
```
## Don'ts
- Don't add `bun build --compile` — incompatible with the shared runtime.
- Don't bundle wholesale `node_modules/` into the .deb; rely on
`SHARED_EXTERNALS` resolution + manifest `nodeApp.privateRuntime`.
- Don't change `app_type` to `"standalone"` unless this app legitimately
needs its own systemd unit (LCD daemons, OTA controllers, etc.).
- Don't fetch cross-origin from the UI. Backend requests must use the
same-origin `/api/v2/node-apps/{{name}}/api/...` path; the platform's
reverse-proxy handles the rest.