Manifest

Every Egglet ships a manifest.json alongside its entry module. The manifest describes who the Egglet is, what it owns, and what runtime backings it uses. The host reads the manifest once on activation and enforces it for the life of the process.

Required fields

An Egglet manifest must include these fields. Everything else is optional.

FieldTypePurpose
idstringStable, unique id. Convention: lowercase, single word, matches the folder name.
versionstringSemVer. Visible in Settings. See Versioning.
labelstringHuman-readable name shown in menus and Settings.
routestringPath the Egglet mounts at, like /hello. The Egglet owns this route.
entrystringRelative path to the entry module, like ./index.ts.

Recommended but optional:

FieldTypePurpose
iconstringLucide icon name. Shown in menus.
descriptionstringOne sentence shown on the Egglet’s card in Settings.
menu"auto" or "hidden"Default visibility in user-facing menus. The user’s per-Egglet toggle in Settings overrides this. Defaults to "auto".

The uses block

The uses block names every runtime backing the Egglet relies on. The host checks each ctx.* call at runtime: a call to a backing that is not declared throws with a clear error pointing at the field to add. An Egglet that needs no platform backings beyond logging, routing, and tool registration can omit uses entirely.

// All four sections of the uses block.
"uses": {
  "llm":      { "role": "agent-secondary", "capability": "text" },
  "gateway":  { "schedules": true, "syncs": true, "headless": true, "writes": true },
  "network":  { "fetch": true },
  "eggletIpc": true
}

uses.llm

Declared when the Egglet calls ctx.ai.text.chat.complete() or ctx.ai.text.chat.stream(). Object shape:

SubfieldValuesDefault
role"agent-primary" or "agent-secondary""agent-secondary"
capability"text" or "vision""text"

Both subfields are optional. The Egglet picks the slot it draws from. Which provider serves the slot is a user setting, not the Egglet’s choice.

uses.gateway

Declares the Egglet’s dependency on the Gateway daemon. Each subfield is an explicit boolean.

SubfieldWhat declaring it means
schedulesThe Egglet registers ctx.alwaysOn.poller() handlers driven by the Gateway scheduler.
syncsOne or more of the Egglet’s tables has a sync_category set, so writes participate in cross-device sync.
headlessThe Egglet calls ctx.headless.fetch().
writesThe Egglet calls ctx.storage.exec(). Reads (storage.all, storage.one) do not require this declaration.

None of these are auto-derived from other fields. The manifest is the source of truth.

uses.network

Declares in-process outbound HTTP. Currently a single subfield:

SubfieldWhat declaring it means
fetchThe Egglet calls ctx.fetch(). This routes through the platform’s in-process HTTP client and is distinct from uses.gateway.headless, which spawns a hidden browser.

uses.eggletIpc

A single boolean. Set true if the Egglet calls ctx.eggletIpc.send() or ctx.eggletIpc.subscribe().

Tables and tools

Tables

The tables array declares every table the Egglet reads from or writes to. Each entry has a logical name (used in SQL placeholders) and either a schema file or an external physical name.

"tables": [
  { "name": "log",     "schema": "./schemas/log.sql" },
  { "name": "items",   "schema": "./schemas/items.sql", "sync_category": "items" },
  { "name": "feeds",   "physical_name": "external_feeds_table" }
]

An entry with schema declares an owned table whose CREATE statement lives in the named SQL file. The host runs schema files at activation and re-runs them on every boot, so they should be idempotent (CREATE TABLE IF NOT EXISTS). An entry with physical_name binds the logical name to a table that exists outside the Egglet, useful during a port; that table is not owned and is not dropped on uninstall.

If any table has sync_category set, uses.gateway.syncs must also be declared. Activation fails otherwise.

Tools

The tools array declares every tool name the Egglet plans to register. The names listed here must match the names passed to ctx.tools.register(); tools registered without a manifest entry are rejected.

"tools": [
  { "name": "feed.poll",        "external": true,  "params": "./tools/poll.json" },
  { "name": "feed.unreadCount", "external": true },
  { "name": "feed.private" }
]

Set external: true when the tool is callable from another Egglet or from the agent harness. Tools without external are private to the registering Egglet. params is an optional path to a JSON Schema describing the tool’s arguments.

Refs and credentials

Refs

An Egglet that owns one or more ref kinds (the @kind:id handle system) declares them in the refs array.

"refs": ["e", "c"]

Each entry is a short kind id. The Egglet’s activate() must call ctx.refs.register() for every kind listed. A kind already claimed by another Egglet causes activation to fail. See the Refs system for details on how refs travel.

Credentials

The credentials array names every credential source the Egglet may read through ctx.credentials.get(). Sources not listed here cannot be read; the host throws.

"credentials": ["github", "reddit"]

Egglets cannot create or revoke credentials. That path lives in Settings, where the user makes the trust decision.

Permissions

An Egglet that calls tools registered by another Egglet must declare each tool name in permissions.

"permissions": ["people.count", "people.get"]

Self-calls (calling tools registered by the same Egglet) do not need an entry. Calls to tools owned by another Egglet without a matching entry are refused. Tools with external: false on the owner side cannot be called cross-Egglet regardless of what the caller declared.

Validation

The host checks the manifest at activation time and refuses to load Egglets that are internally inconsistent. The most common failures:

When an Egglet fails to activate, Settings shows the failure reason on the Egglet’s card. Reload the Egglet after fixing the manifest to clear the error without restarting Egg.