Apps Overview
An App is an agent with its own UI. It bundles a persona, a complete frontend application, and an optional native sidecar binary into a standalone application that opens in its own window. Apps are the right choice when chat output isn't enough — dashboards, contact managers, journals, deal trackers.
Apps are complete frontend applications — not just HTML pages. Build with any framework: React, Vue, Solid.js, SvelteKit, HTMX, or vanilla JavaScript. The Nebo App SDK provides storage, identity, agent invocation, and CORS-free HTTP proxying regardless of your framework choice.
App SDK
Storage, identity, agent invocation, chat embed, fetch proxy.
App Sidecar
Native binary backend via gRPC for compute-heavy apps.
Examples
10 production apps: React, Vue, Solid, SvelteKit, HTMX, vanilla JS.
When to Build an App vs. a Skill
| Need | Build | Why |
|---|---|---|
| Teach the agent a new skill via instructions | Skill | No UI needed -- chat output suffices |
| Visual dashboard with charts and tables | App | Needs rendered UI |
| Contact list with search and edit | App | Interactive CRUD UI |
| Journal with custom layout | App | Persistent visual state |
| Email drafting with templates | Skill | Chat + tool calls handle it |
App Directory Structure
my-app/
├── AGENT.md # Required -- persona and instructions
├── manifest.json # Required -- identity, permissions, window config
├── agent.json # Optional -- workflows, skills, user inputs
├── ui/ # Required -- static frontend files
│ ├── index.html # Entry point
│ ├── style.css
│ └── app.js
├── skills/ # Optional -- skill docs that teach the agent how to use tools
│ ├── workspace-mgmt/
│ │ └── SKILL.md
│ └── document-analysis/
│ └── SKILL.md
├── sidecar/ # Optional -- native backend (Rust recommended)
│ ├── Cargo.toml
│ ├── src/main.rs
│ └── target/release/
│ └── my-app-sidecar
└── <data_dir>/appdata/agents/{{id}}/ # Auto-created at runtime -- persistent dataWhat Each File Does
| File | Purpose |
|---|---|
AGENT.md | The agent's persona. Written in markdown. Determines how the agent responds when invoked from the app. |
manifest.json | Identity, version, permissions, window dimensions. An optional "type": "app" field marks the agent as an app. |
agent.json | Operational wiring -- workflows, skill references, event bindings, user inputs. Same format as any agent. |
skills/ | SKILL.md files that teach the agent how and when to use the sidecar tools. Loaded automatically at launch. |
ui/index.html | Entry point for the app's frontend. Served at /apps/{{agent_id}}/ ui/index.html. |
sidecar/ | Optional native binary. Runs as a gRPC service on a Unix socket. Nebo proxies requests to it. |
manifest.json
{
"id": "deal-tracker",
"name": "@acme/agents/deal-tracker",
"version": "1.0.0",
"description": "Track real estate deals with AI-powered analysis.",
"type": "app",
"permissions": [
"storage:readwrite",
"subagent:invoke",
"network:outbound"
],
"window": {
"title": "Deal Tracker",
"width": 1024,
"height": 768,
"resizable": true
}
}Required Fields
| Field | Description |
|---|---|
id | Unique identifier. Must match the directory name. |
name | Qualified name (@org/agents/name) or display name. |
version | Semantic version (1.0.0). |
type | Optional. Set to "app" to mark this agent as an app (drives UI window handling). |
Optional Fields
| Field | Description |
|---|---|
description | One-line description shown in the marketplace and apps page. |
permissions | Array of permission strings (see below). |
window | Default window dimensions and title. |
Permissions
Permissions use prefix:scope format. Declare only what your app needs.
| Permission | What It Grants |
|---|---|
storage:readwrite | Read and write to the app's scoped KV store |
subagent:invoke | Invoke other agents via the SDK |
network:outbound | Make HTTP requests through the proxy |
filesystem:read | Read files from the user's system |
shell:execute | Run shell commands |
memory:read | Read agent memories |
oauth:google | OAuth flow for Google services |
Window Config
| Field | Default | Description |
|---|---|---|
title | app name | Window title bar |
width | 1024 | Default width (pixels) |
height | 768 | Default height (pixels) |
resizable | true | Allow user resize |
Nebo remembers window position and size per app -- the user's last arrangement is restored on reopen.
AGENT.md -- The Persona
The AGENT.md defines how the agent behaves when invoked from the app. Same format as any agent persona.
# Deal Tracker You are a real estate deal analyst. When the user asks you to analyze a deal, examine the financials, compare with market data, and provide a recommendation. ## Capabilities - Analyze deal financials (cap rate, cash-on-cash, IRR) - Compare properties against market comps - Track deal pipeline stages - Generate investment memos ## Guidelines - Always show your math - Flag deals with cap rates below 5% as high-risk - Use conservative assumptions for projections
agent.json
The agent.json file wires together workflows, skill references, event bindings, and user inputs. It uses the same format as any Nebo agent. See the Agents guide for the full specification.
{
"skills": [
"skills/workspace-management",
"skills/document-analysis",
"skills/data-export"
]
}Frontend (ui/)
The ui/ directory contains your app's static frontend. Any framework works -- React, Vue, Svelte, Solid, HTMX, or vanilla HTML/JS. Nebo serves these files as-is.
Entry Point
ui/index.html is the entry point. In the Tauri desktop app, each app opens in its own window using the neboapp:// custom protocol:
neboapp://{{agent_id}}/ This gives each app its own origin with / as the root URL. Your app's assets load from their natural paths (/style.css, /app.js, etc.) -- no base URL configuration needed. Any framework works out of the box.
In the browser fallback, apps are served at /apps/{{agent_id}}/ ui/index.html.
Using the SDK
Install the SDK as an ES module for React, Vue, Svelte, or Solid:
npm install @neboai/app-sdk
Or include the global SDK for HTMX and vanilla HTML:
<script src="/sdk/nebo.global.js"></script>
See the App SDK page for the full API reference.
Framework Notes
The neboapp:// custom protocol gives each app its own origin at /, so all frameworks work without special base URL configuration. For SvelteKit, use adapter-static with output to ../ui. HTMX apps work natively. React, Vue, Solid, and vanilla HTML need no special configuration -- build your app normally and place the output in ui/.
Skills Bundled with Apps
Apps can bundle skills in a skills/ directory alongside the sidecar. Each skill is a directory containing a SKILL.md file with YAML frontmatter and markdown instructions that teach the agent how and when to use each tool.
--- name: workspace-management description: Manage projects, documents, and folders triggers: - create a project - list projects - upload document - workspace stats --- # Workspace Management Tools for managing projects, documents, and folders. ## list_projects List all projects for the current user. - **Method:** GET /projects - Returns: Array of project objects with id, name, description, created_at ## create_project Create a new project. - **Method:** POST /projects - **name** (string, required): Project name - **description** (string, optional): Project description - Returns: Created project object
Skills are loaded automatically when the app's sidecar launches and unloaded when the sidecar shuts down. Reference them in agent.json using the path prefix (e.g. "skills/workspace-management"). Nebo matches the last path segment against the name: field in the SKILL.md frontmatter.
Complete Example: Journal App
A minimal app with no sidecar. The agent provides AI reflection, storage persists the last entry, the embedded chat gives the user a full conversational interface, and identity lets the UI adapt to the agent's configuration.
manifest.json
{
"id": "journal",
"name": "@nebo/agents/journal",
"version": "1.0.0",
"description": "AI-powered journal with reflection prompts.",
"type": "app",
"permissions": ["storage:readwrite", "subagent:invoke"],
"window": {
"title": "Journal",
"width": 700,
"height": 800,
"resizable": true
}
}AGENT.md
# Journal You are a thoughtful journaling companion. When the user writes an entry, read it carefully and offer a brief, insightful reflection. Don't summarize -- add depth. Ask one follow-up question that helps them think more deeply about what they wrote.
ui/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="nebo-app-id" content="journal" />
<script src="/sdk/nebo.global.js"></script>
</head>
<body>
<h1>Journal</h1>
<textarea id="entry" placeholder="What's on your mind?"></textarea>
<button onclick="reflect()">Reflect</button>
<div id="reflection"></div>
<div id="chat"></div>
<script>
// Mount the embedded chat
nebo.chat.mount(document.getElementById('chat'), {
placeholder: 'Reflect on your day...',
height: '300px'
});
// Use identity to personalize the UI
nebo.identity.get().then(agent => {
document.querySelector('h1').textContent = agent.displayName;
});
async function reflect() {
const entry = document.getElementById('entry').value;
const { text } = await nebo.agents.invoke(entry);
document.getElementById('reflection').textContent = text;
await nebo.storage.setItem('last-entry', entry);
}
</script>
</body>
</html>