Packaging & Marketplace
This guide covers the packaging format for all Nebo marketplace artifacts, artifact naming, and the artifact hierarchy.
Hierarchy
APP > AGENT > WORK > SKILL (UI) (job) (procedure) (knowledge + actions)
This is the design direction — the order in which you think about building. Start with knowledge and actions (Skill), chain those into procedures (Workflow), compose procedures into a job (Agent), and add a dedicated UI when chat isn't enough (App). The > represents conceptual priority, not a runtime dependency.
Each layer auto-installs its dependencies downward:
- App — an Agent with a frontend UI and optional sidecar binary
- Agent — installs Workflows and Skills declared in
agent.json - Workflow — installs Skills declared in
workflow.jsondependencies - Skill — leaf node. No dependencies to auto-install.
Plugins are shared native binaries that bundle skills and provide platform capabilities (Google Workspace CLI, browser automation, etc.). They sit alongside the hierarchy — skills and agents declare which plugins they need.
Platform Capabilities (storage, network, vision, calendar, email, browser) are provided by Nebo itself — they are infrastructure, not marketplace artifacts. Skills declare which capabilities they need; the platform provides them. See Platform Capabilities.
Artifact Naming
Every artifact has two identifiers: a qualified name (canonical, for engineering) and an install code (alias, for sharing).
Qualified Names
The qualified name is the canonical identifier for every artifact. It is human-readable, org-scoped, type-aware, and versioned.
Format: @org/type/name@version
@acme/skills/sales-qualification@1.0.0 @acme/skills/crm-lookup@1.0.0 @acme/workflows/lead-qualification@2.1.0 @nebo/agents/executive-assistant@1.0.0
Read left to right: who published it, what kind of artifact it is, what it's called, what version.
| Segment | Description | Rules |
|---|---|---|
@org | Publisher org (scoped) | Lowercase, alphanumeric + hyphens |
type | Artifact type | skills, workflows, agents |
name | Artifact name | Lowercase, alphanumeric + hyphens |
@version | Semver version (optional) | Omit for latest; supports semver ranges |
Version Resolution
| Reference | Resolves To |
|---|---|
@acme/skills/crm-lookup | Latest published version |
@acme/skills/crm-lookup@1.0.0 | Exact version 1.0.0 |
@acme/skills/crm-lookup@^1.0.0 | Compatible with 1.0.0 (>=1.0.0 <2.0.0) |
@acme/skills/crm-lookup@>=1.0.0 | Any version 1.0.0 or higher |
Semver range semantics follow npm conventions. Exact versions pin to a specific release. Caret (^) allows patch and minor updates. Tilde (~) allows patch updates only.
Namespacing: Two publishers can build artifacts with the same name without collision:
@acme/skills/crm-lookup @nebo/skills/crm-lookup
Different artifacts, no conflict. The org scope prevents name collisions as the marketplace grows.
Install Codes (Aliases)
Install codes are short, shareable aliases assigned by the marketplace. They exist for one purpose: so a non-technical user can text a code to a friend and have it work.
Format: PREFIX-XXXX-XXXX — Crockford Base32, case-insensitive.
| Prefix | Artifact | Example |
|---|---|---|
NEBO | Link bot to NeboAI account | NEBO-A1B2-C3D4 |
SKIL | Install a skill | SKIL-R7KP-2M9V |
WORK | Install a workflow | WORK-5TG2-XBJK |
AGNT | Install an agent | AGNT-SNTW-WY0B |
APPS | Install an app | APPS-3FKT-7WNP |
COLL | Install a collection (bundle of artifacts) | COLL-9DCE-4MPA |
CONN | Install a connector (MCP) | CONN-4HVT-8KRP |
LOOP | Join bot to a Loop | LOOP-7YSR-6WN3 |
PLUG | Install a plugin | PLUG-4HVT-8KRP |
Install codes always resolve to @latest. They are detected case-insensitively in chat messages and dispatched automatically.
workflow.json, agent.json, dependency declarations, and all structured data — use qualified names. Install codes are for users, not for code.Package Format
Skills — Directory Format (Agent Skills Standard)
Skills follow the Agent Skills standard. A skill is a directory containing a SKILL.md file and optional supporting resources. No manifest.json is required — the SKILL.md frontmatter is the source of truth.
skill-name/ ├── SKILL.md # Required — YAML frontmatter + markdown instructions ├── scripts/ # Optional — executable scripts (Python, TypeScript) ├── references/ # Optional — docs loaded on demand ├── assets/ # Optional — templates, images, fonts └── examples/ # Optional — sample data, examples
Skills from Anthropic, OpenAI, OpenClaw, and other Agent Skills-compatible platforms can be installed directly with no modification. See Skills for the full format spec.
Apps — Agent + Frontend UI
Apps are agents with their own UI. They use the agents qualified name type with "type": "app" in their manifest.
my-app/ ├── AGENT.md # Required — persona + instructions ├── manifest.json # Required — identity, permissions, window config ├── agent.json # Optional — workflows, skills, pricing ├── ui/ # Required — static frontend │ ├── index.html │ ├── style.css │ └── app.js └── sidecar/ # Optional — native backend binary
The manifest.json extends the standard agent manifest with app-specific fields:
{
"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
}
}| Field | Description |
|---|---|
type | Optional. Set to "app" to distinguish an app from a regular agent (drives UI window handling) |
permissions | Capabilities the app requires (storage, network, subagent, etc.) |
window | Default window dimensions and title for the desktop app |
Workflows and Agents — .napp Archive
Workflows and agents are distributed as .napp files — signed tar.gz archives.
@acme/workflows/lead-qualification-1.0.0.napp → manifest.json → workflow.json → WORKFLOW.md @nebo/agents/executive-assistant-1.0.0.napp → manifest.json → agent.json → AGENT.md → skills/ → skills/crm-lookup/SKILL.md
.napp Envelope Format
Every .napp file wraps the inner tar.gz in a binary envelope that is verified before the payload is touched:
[4B magic "NAPP"] [1B version 0x01] [64B ED25519 signature] [32B SHA256 hash] [payload...]
Verification order:
- SHA256 — cheap integrity check of the payload bytes.
- ED25519 — proves the archive was signed by NeboAI. The signature covers
hash || payload.
The NeboAI public key is embedded at compile time so verification works offline (first launch, air-gapped installs). A SigningKeyProvider also fetches the key from GET /api/v1/apps/signing-key with a 24-hour cache for key rotation.
Sealed Archives
For workflows and agents, the .napp is never extracted. Nebo reads files directly from the archive at runtime. The signed archive is the running artifact — if someone tampers with the file, the next read fails signature verification. This provides continuous integrity, not just point-in-time verification at install.
| Artifact | Storage | Integrity Model |
|---|---|---|
| Skill | Directory on disk (marketplace: sealed .napp) | Frontmatter is source of truth; marketplace archives are signed |
| Workflow | .napp sealed | Archive is the signed artifact — continuous integrity |
| Agent | .napp sealed | Archive is the signed artifact — continuous integrity |
| App | Directory (dev) / .napp sealed (marketplace) | Same as agent — includes ui/ directory contents |
License-Key Sealed Archives
Paid marketplace artifacts use an additional encryption layer on top of the .napp envelope. After envelope verification, the inner payload is AES-256-GCM encrypted with a per-artifact license key:
.napp envelope → unwrap (verify ED25519 + SHA256) → sealed payload → unseal (AES-256-GCM) → plain tar.gz
Key derivation: derive_license_key(master_secret, artifact_id) uses HKDF-SHA256 with the artifact ID as salt and neboai-license-v1 as info. The same key works regardless of who holds the license — authorization is server-side, so .napp files never need re-download on license transfer.
Detection: Plain .napp payloads start with gzip magic bytes (0x1f 0x8b). Sealed payloads start with a 12-byte random nonce, which won't match. is_sealed() checks this.
Partial extraction: For sealed skill archives, only executables (scripts/, bin/, binary) and metadata (manifest.json, plugin.json, signatures.json) are extracted to disk. IP-sensitive files (SKILL.md, references, assets) stay inside the sealed .napp and are read in memory at runtime — plaintext never touches disk.
Versioned Storage
Artifacts are stored by qualified name and version:
nebo/ # From NeboAI marketplace
skills/
@acme/skills/sales-qualification/
1.0.0.napp
1.1.0.napp
workflows/
@acme/workflows/lead-qualification/
1.0.0.napp
agents/
@nebo/agents/executive-assistant/
1.0.0.napp
user/ # User-created (dev/sideload path)
skills/
my-custom-skill/
SKILL.md # Loose directory — no archive
scripts/
references/
agents/
my-agent/
agent.json
AGENT.md
skills/
my-app/
manifest.json # type: "app"
AGENT.md
ui/
index.html
sidecar/Marketplace artifacts (nebo/) are sealed .napp files. Signed, versioned, read from archive at runtime.
User artifacts (user/) are loose files on disk. No archive, no signatures. This is the development path — edit directly, hot-reload picks up changes.
Runtime Data — appdata/
Artifact runtime data (databases, caches, user files) lives in a completely separate tree:
appdata/ # Runtime data — NEVER touched by updates
plugins/
gws/ # Plugin data (survives all version upgrades)
cache.db
sidecar.log
skills/
my-custom-skill/ # Skill data (survives reinstalls)
output.json
agents/
deal-tracker/ # Agent app data (survives upgrades)
deals.db
sidecar.logThis follows the iOS model: code and data live in physically separate containers. The update system operates on nebo/ and user/ but never touches appdata/. Artifacts own their data and are responsible for their own schema migrations across versions.
Version Resolution
When a qualified name with a version range is referenced, the resolution logic is:
- Scan installed versions under that qualified name
- Pick the highest version satisfying the semver range
- If nothing matches, fetch from NeboAI marketplace
- If fetch fails and nothing is installed, error
This resolution applies everywhere a versioned qualified name appears — workflow dependencies, activity skills arrays, agent workflows refs, and agent-level skills arrays.
manifest.json
Workflows, agents, and apps ship a manifest.json as their marketplace identity envelope. Skills do not use manifest.json — their identity comes from SKILL.md frontmatter.
{
"name": "@acme/workflows/lead-qualification",
"version": "1.0.0",
"description": "Qualifies inbound leads through research, scoring, and routing",
"signature": {
"algorithm": "ed25519",
"key_id": "nebo-prod-2026"
}
}Fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Qualified name: @org/type/artifact-name |
version | string | yes | Semantic version |
description | string | no | Short description |
tags | string[] | no | Categorization tags |
signature | object | no | ED25519 signing metadata |
The name field is the canonical identifier. The org, type, and artifact name are all parsed from it. There is no separate id, type, or author field — the qualified name carries all of that.
Separation of Concerns
Each artifact type has a clear split between identity, domain logic, and prose:
- Skills —
SKILL.mdfrontmatter (identity + runtime config) + body (knowledge) + optional bundled resources. No manifest.json. Compatible with the Agent Skills standard. - Workflows —
manifest.json(marketplace identity) +workflow.json(procedure definition) +WORKFLOW.md(agent docs). Sealed archive. - Agents —
AGENT.md(persona prose, the only required file) + optionalagent.json(event bindings, pricing, defaults, sidecar tools, scopes, memory config) + optionalmanifest.json(marketplace identity) + optional bundled skills. Sealed archive. - Apps — everything an Agent has +
"type": "app"in manifest +ui/directory (static frontend) + optional sidecar binary.
Quick Reference
Timeout Constants
| Operation | Timeout |
|---|---|
| Hook call | 500 ms |
| Hook circuit breaker | 3 consecutive failures |
| Hook circuit breaker recovery | 5 minutes |
| Signing key cache | 24 hours |
| Revocation cache | 1 hour |
| Plugin tool default timeout | 120 seconds |
| Plugin max timeout (permissions) | 300 seconds |
Qualified Name Format
@org/type/name@version — org and name are lowercase alphanumeric + hyphens. Type is one of: skills, workflows, agents. Version follows semver.
Install Code Format
PREFIX-XXXX-XXXX — Crockford Base32, case-insensitive. Always resolves to @latest.
Prefixes: NEBO, SKIL, WORK, AGNT, APPS, COLL, CONN, LOOP, PLUG