Build your first app

Scaffold, run inside Iro with hot reload, then pack and publish.

Scaffold

npm create iro makes a project; with no name, it makes it in the current folder, which must be empty:

npm create iro my-app
cd my-app
npm install

A project is standalone: its own package.json, tsconfig.json, Vite config, tests and iro.json. It imports only the public entry points of @iro-labs/sdk, @iro-labs/client and @iro-labs/cli — never the kernel or a path outside its folder (lint-enforced).

Run it in Iro

Turn on Settings → Developer mode, then:

iro dev

Link the project through Iro’s consent sheet (source Development) and the UI loads from the project’s Vite server with hot reload. Services and jobs reload on save. The developer socket exists only while Developer mode is on; it grants nothing without the sheet.

Minimal service (service/service.ts):

import { defineService } from '@iro-labs/sdk'

export default defineService({
    async 'notes@1/listNotes'(ctx, input) {
        return { notes: [] }
    },
})

Minimal UI call:

import { createClient } from '@iro-labs/client'
import manifest from '../iro.json'

const iro = createClient(manifest)
const notes = await iro['notes@1'].listNotes({})

Jobs are separate entry points with defineJob; they get createClient without presentation APIs.

Build, validate, pack

iro build
iro validate
iro pack --sign --key ~/.config/iro/publisher-key.json

iro build produces checked contract JSON plus the UI bundle and service file. iro types generates .iro/contracts.d.ts for consumers via AppContracts declaration merging. Contract files are checked against the schema subset (closed objects, bounded strings/arrays, local $refs).

Publish to a catalog

iro catalog add ./catalog.json ./my-app-1.0.0.iropkg --url https://example.com/packages/my-app-1.0.0.iropkg

Commit to the catalog repo. The Store installs it through the same download, checks and sheet as any link.