App Structure & app.yaml
How a Rome App source tree is laid out, and what the app.yaml manifest declares.
Agent-readable version: Markdown · llms.txt
A Rome App is a source tree with one manifest at the root: app.yaml. The
manifest declares the app identity and lists the pieces Rome should load. Files
that are not listed are just files; they are not registered as actions, agents,
skills, hooks, APIs, databases, or web surfaces.
Source Tree
The default scaffold has this shape:
<app-root>/
├── app.yaml
├── README.md
├── .gitignore
├── package.json
├── pnpm-workspace.yaml
├── tsconfig.json
├── components.json
├── drizzle.config.ts
├── .rome_store/
│ └── rome_store.yaml
└── src/
├── actions/<name>/
│ ├── action.yaml
│ └── index.ts
├── agents/<name>.yaml
├── api/index.ts
├── assets/icon.svg
├── db/
│ ├── schema.ts
│ ├── migrations/
│ └── repositories/
├── skills/
└── web/
├── App.tsx
├── components/
├── lib/
└── styles.cssOnly app.yaml is required for the app model. The rest depends on which
capabilities the app actually ships. The scaffold does not create a
src/hooks/ directory and leaves src/skills/ empty — add
src/hooks/<name>/ and src/skills/<name>/SKILL.md (plus their manifest
entries) when the app needs them.
README.mdis user-facing App Store listing copy..gitignorekeepsnode_modules/,dist/, and.rome/out of git.package.json,pnpm-workspace.yaml, andtsconfig.jsondefine the app workspace and build.components.jsonis used by the scaffolded web UI component setup.drizzle.config.tsis needed only when the app uses a database..rome_store/rome_store.yamlcontains store-side metadata used when publishing; it is separate from the runtime manifest.src/assets/icon.svgis usually referenced byapp.yamlasicon: assets/icon.svg.
Delete unused scaffold files together with their manifest entries. For example,
if the app has no database, remove src/db/, drizzle.config.ts, DB scripts
and dependencies, and the db: block.
Build Output
Most source apps set:
appRoot: distWith that layout, source files live under src/, and pnpm build writes the
runtime app into dist/. Rome loads declared artifacts from the effective app
root, so after build these manifest paths resolve under dist/:
web:
manifest: web/manifest.json
api:
entry: api/index
actions:
- actions/helloDo not edit dist/ by hand. Source-mode install rebuilds and repacks the app.
Manifest
app.yaml is strict. Unknown fields are rejected. New apps use
formatVersion: 2; version 1 remains readable only for legacy bundles. Strict
does not mean minimal, though: the schema also accepts a small set of optional
store-listing fields (author, long_description, screenshots, category,
homepage, repository).
A typical app looks like this:
formatVersion: 2
id: invoice-tracker
name: Invoice Tracker
version: 0.1.0
description: Track invoices, payments, and follow-ups.
icon: assets/icon.svg
appRoot: dist
web:
manifest: web/manifest.json
displayName: Invoice Tracker
navLabel: Invoices
api:
entry: api/index
actions:
- actions/create-invoice
- actions/sync-payments
agents:
- agents/invoice-assistant.yaml
skills:
- skills/invoice-triage
hooks:
- hooks/channel-message
components:
- invoice-card
db:
migrations: db/migrations
tablePrefix: invoice_trackerIdentity Fields
| Field | Required | Notes |
|---|---|---|
formatVersion | yes | Use 2 for new apps. Version 1 is read only for legacy compatibility. |
id | yes | The machine id: lowercase letters, numbers, hyphens, starts with a letter, max 64 chars. core and self are reserved. Used in install state, paths, URLs, artifact ids, and default table naming. |
version | yes | Valid SemVer, such as 0.1.0. |
description | yes | Short app summary used by Rome and store surfaces. |
name | no | Human display name. Use Title Case words, not the kebab-case id. |
icon | no | Path relative to appRoot, usually assets/icon.svg after build. |
appRoot | no | Effective runtime root. Built apps normally use dist. Required in practice for any app built with the rome CLI — the build fails without it. |
Capability Fields
Each capability block is optional. Add only what the app uses.
| Field | Purpose |
|---|---|
web | React web surface mounted by the dashboard. manifest points to the built web manifest; displayName and navLabel control host labels. |
api | App-owned HTTP API. entry defaults to api/index when omitted. |
actions | Action directories, each with action.yaml and index.ts. |
agents | App-owned agent YAML files. |
skills | Skill directories, each usually containing SKILL.md. |
hooks | Runtime hook directories. |
components | Inline component ids the web bundle registers and may render in chat. |
db | Per-app SQLite migrations and table prefix. |
suggestedChannelBindings | Optional setup hints that suggest routing a channel to one of the app's agents. |
actions, agents, skills, and hooks entries are normally plain paths:
actions:
- actions/helloArtifact Names and References
Agent, action, and skill definitions declare an app-local name matching
^[a-z][a-z0-9_-]{0,63}$. A definition name never contains :. Rome combines
the manifest App ID and local name into the runtime identity:
<app-id>:<local-name>References and runtime calls always use that canonical ID, including references
to an artifact owned by the same app. For an app with id: invoice-tracker, an
action definition might say name: create_invoice, while an Agent allowlist,
Routine target, or runAction call uses invoice-tracker:create_invoice.
# src/agents/invoice-assistant.yaml
name: invoice-assistant
actions:
- invoice-tracker:create_invoice
allowedSubagents:
- assistant:exploreNever write a bare artifact reference or self:<local-name>. Store listing IDs
such as @publisher/invoice-tracker are also not runtime artifact namespaces;
the manifest App ID remains the owner.
Manifest entries can use object form for compatibility metadata:
actions:
- path: actions/create-invoice
publicName: create_invoiceIn format version 2, publicName, when present, must match the definition's
local name. aliases and legacyPath exist for legacy bundles and do not create
new bare-name behavior for version 2 apps.
API Options
By default, app API requests require a session. App web UIs call the dashboard
surface at /api/apps/<appId>/... through fetchAppApi. Public callbacks and
third-party webhooks should use /api/app-api/<appId>/...; those public
subpaths can opt out with api.noAuth:
api:
entry: api/index
noAuth:
- /webhooks/stripe
- /public/*noAuth: trueopens every public app-api subpath.noAuth: ["/path", "/prefix/*"]opens only those public subpaths.- Omit it for normal dashboard-only APIs reached by
fetchAppApi.
api.relayWebhook is for apps that consume the core webhook relay. Most apps do
not need it.
Database Options
The db: block enables app-owned SQLite migrations:
db:
migrations: db/migrations
tablePrefix: invoice_trackertablePrefix must be a SQL-safe lowercase identifier: lowercase letters,
numbers, and underscores, starting with a letter. If omitted, Rome uses the app
id as the prefix; when the app id contains hyphens, set tablePrefix
explicitly with underscores. Real app tables are namespaced so they do not
collide with system tables or other apps.
Validation Rules
Rome validates the manifest at pack/install time.
- Unknown manifest fields fail validation.
formatVersion,id,version, anddescriptionare required.idmust be a valid app id.- Format version 2 action, agent, and skill names must be valid local names.
- Format version 2 Agent
actionsandallowedSubagentsentries must use canonical<app-id>:<local-name>references (except the special"*"action allowlist). versionmust be valid SemVer.- Paths listed in the manifest must exist in the effective app root after build.
api.noAuthpath entries must start with/.db.tablePrefix, when set, must match the table-prefix rule.
The main rule is simple: the manifest and filesystem must agree. When you add a capability, add both its files and its manifest entry. When you remove a capability, remove both.
Next
- Use Quickstart for the scaffold and install flow.
- Use Capabilities for action, agent, API, database, and web UI implementation details.
- Use Design Guidelines for product, mobile, icon, and README rules.