Getting started
Apps/Start/Getting started

Getting started

Install ib-apps, create an app, and keep a folder on your computer in sync with its development environment.

Install the CLI

The CLI needs Node.js 20 or later.

npm i -g @inferencebureau/apps
ib-apps --help

The app's Settings → API keys page shows the same commands.

Sign in

ib-apps login opens the dashboard in your browser. You approve the sign-in and pick a team; the CLI receives a session that acts as you in that team (OAuth with PKCE to a loopback address, so no key is copied anywhere). It is saved in your user config folder (~/.config/inferencebureau/apps.json, or under %APPDATA% on Windows), readable only by you. ib-apps logout revokes it.

In CI, skip login and set IB_API_KEY to a key linked to the app; it takes precedence over a saved sign-in. See API keys. IB_BASE_URL points the CLI at another API origin.

Create an app

Create one from the dashboard (Apps → New app, which then walks through setup) or from the terminal:

ib-apps create "Inference Bureau Wholesale" --platform shopify
cd inference-bureau-wholesale
ib-apps dev

create makes the app with its development environment and downloads the starter files into a new folder named after the slug. Creating apps needs a sign-in or a key with Apps Permissions. A web app starts as a Members app, or with --starter site as a site with no sign-in; --recipe <id> starts either platform's app from one of the example apps instead. create makes a custom Shopify app; a public one is created in the dashboard (choose Public on New app) or with POST /v1/apps and distribution: "public", then pulled. A Shopify app also needs its Shopify apps, which are created in the dashboard: see Connect to Shopify.

Sync with development

ib-apps dev syncs once, then watches the folder: local saves are pushed, and changes from the dashboard editor or a teammate are pulled every two seconds. For team owners and admins it also runs the dev server on your computer (below); otherwise a save under web/ is written straight into development's hosted dev server, which hot-reloads it. Either way it prints the address to open the app at. A save under extensions/ releases the storefront blocks to development's Shopify app.

While it runs, the dashboard's Terminal panel (in Logs) shows your computer as connected, with each file it sent and received.

  • Conflicts. A file changed both here and on the server since the last sync is never overwritten. In a terminal you choose: keep yours, keep the server's, see a diff, or skip. Outside a terminal it is skipped; rerun with --prefer local or --prefer remote.
  • What isn't synced. .git, .ib, node_modules, .next, .vercel, .turbo, .DS_Store, files over 50 MiB, and anything listed in .ibignore (one entry per line; a bare name matches anywhere, an entry with a slash matches that path).
  • Limits. An environment holds up to 2,000 files, with paths up to 512 characters. A push carries up to 200 changes; the CLI splits bigger ones.
  • Large files. Files over 1 MiB, up to 50 MiB (images, fonts, video), are uploaded to storage on their own, then pushed by their hash; the CLI does both. A file is stored once per team however many environments hold it, and counts toward storage at its size. Builds, the dev server and extension releases get its bytes as usual; the dashboard's editor shows it as binary.
  • Sync state. .ib/sync.json remembers the last synced version and hashes. Keep it out of git; the starter .gitignore does. Git is optional: the environments run from Inference Bureau's copy, not from a repository.

Run the dev server on your computer

Development has a hosted dev server that wakes when needed (Limits), and it serves everyone who isn't at a terminal: the dashboard editor and assistant, Preview, Shopify installs and webhooks, development schedules, a teammate opening the address. While you work, ib-apps dev runs the dev server on your own computer instead when you're a team owner or admin, so you get your debugger, your tools and no wake, and everything else stays the same: the app is still opened at its development address, actions, Shopify webhooks, the app proxy and jobs still reach it, and your folder still syncs both ways. When it can't (you're a member, no tunnel could be opened, next dev didn't start), it says so in one line and syncs with the hosted dev server as before.

ib-apps dev                  # on this computer when it can, else hosted
ib-apps dev --hosted         # only sync; the hosted dev server serves the app
ib-apps dev --local          # must run here; fails rather than falling back
ib-apps dev --port 3100
ib-apps dev --url https://my-tunnel.example.com   # a tunnel you run yourself
  • What runs. The CLI mirrors your folder into .ib/local laid out as a build is (web/ at the root, api/ under ib/) and takes the generated files from the server: the SDK, the actions registry and route, the Next.js config and package.json. Then it installs and runs next dev there with the environment's variables. Every save updates the mirror, and a changed package.json reinstalls and restarts it.
  • How it's reached. A Cloudflare quick tunnel (downloaded once, no account) gives your computer a public address; pass --url to use a tunnel of your own. Visitors, Shopify and the API use the environment's own address as always; the tunnel's is only shown in your terminal. Every connection report (each 5 seconds) tells the server your computer is serving the environment. Ctrl-C (or closing the terminal) hands it back to the hosted dev server, and so does a minute without a report if the CLI is killed; the tunnel and next dev end with the CLI either way. The CLI also checks, every half minute and as soon as your computer wakes from sleep, that the tunnel still reaches it: a quick tunnel that closed while the computer slept is handed back at once and opened anew (next dev restarts behind it), and a tunnel of your own is served again once it answers.
  • Who can. It reveals the environment's variables, so it takes a team owner or admin, or a key with Apps Permissions, and each start is in the audit log. Members sync with the hosted dev server. The variables are read when it starts: after changing them in the dashboard, restart it.
  • While it runs, the hosted dev server isn't written to or woken, no dev server hours are used, and Settings shows the dev server as running on a developer's computer. If a second computer starts serving the same environment, the newer one takes over, so give each developer their own development environment (Settings → Environments).

Commands

ib-apps login                  # sign in with your browser (once per computer)
ib-apps logout                 # sign this computer out
ib-apps whoami                 # who and which team this computer is signed in as
ib-apps list                   # the team's apps
ib-apps create <name>          # create an app and download its starter files into ./<slug>
ib-apps pull                   # download the app's files into this folder (a fresh copy)
ib-apps push                   # sync once: take the server's changes, then send yours
ib-apps status                 # what changed here and on the server since the last sync
ib-apps dev                    # sync both ways while you work; owners and admins get the dev server on this computer
ib-apps dev --hosted           # the same, served by the hosted dev server
ib-apps deploy                 # sync, then ship development to production
ib-apps test                   # run the app's tests (Vitest) against in-memory records

push and status work in a linked folder (one with .ib/sync.json). pull refuses to overwrite local changes without --force. Production's files can be pulled (--env production) but not pushed. deploy is described in Deploys.

Every command exits 0 when it's done, 1 when it failed (a refused deploy, a failed build, a conflict left unresolved) and 2 when it couldn't run (not signed in, not a linked folder, a bad option), so it can run in CI.

Project layout

settings.ib.ts                       # the platform
accessControl/permissions.ib.ts      # roles and what each may read and run
api/
  models/
    <model>/
      schema.ib.ts                   # the model's fields
      actions/<action>.ts            # run, onSuccess, options
  actions/<action>.ts                # global actions
web/
  app/                               # Next.js App Router: pages and route handlers
    proxy/                           # Shopify: the storefront's app proxy routes
  proxy.ts                           # Shopify: the frame-ancestors header for the embedded pages
tests/                               # tests of the actions (ib-apps test); never built
extensions/
  <handle>/                          # Shopify: a theme app extension
package.json
vitest.config.ts                     # for ib-apps test
.ibignore                            # optional: more paths never to sync
PathWhat it is
settings.ib.tsThe app's settings: { type: "ib/settings/v1", platform: "shopify" }.
api/models/<model>/schema.ib.tsA model. The folder name is the model's name. See Models.
api/models/<model>/actions/*.tsThe model's actions, one per file, named by the file. See Actions.
api/actions/*.tsGlobal actions, which don't belong to one model.
web/A Next.js App Router app. Its package.json dependencies come from the root package.json.
extensions/Shopify theme app extensions. See Storefront blocks.
accessControl/Roles and their grants (permissions.ib.ts). See Access control.

Code imports @inferencebureau/apps-sdk (and @inferencebureau/apps-sdk/browser in client components). Every build installs the SDK for you, so the starter's package.json lists it like any dependency.

The starter files

Every app starts with README.md, .gitignore, package.json (Next.js, React and the SDK, with Vitest and an npm test script), vitest.config.ts, settings.ib.ts, accessControl/permissions.ib.ts, web/app/layout.tsx and web/app/globals.css, and the coding agent files. A web app is one of two starters, chosen under Starter on New app, with ib-apps create --starter site or with starter: "site" to POST /v1/apps: a Members app (accounts, sign-in and members-only pages) or a Site (no sign-in: a front page led by the latest post, posts written in the dashboard's record editor and served at /blog and as RSS, a contact form whose messages become lead records and an email to you, and a newsletter list with double opt-in). Then, by platform and starter:

ShopifyWeb: Members appWeb: Site
  • api/models/shopifyShop/schema.ib.ts: the store's domain, state, scopes, install dates and the shop's name, email, currency and plan
  • actions/install.ts: saves the record, then writes the storefront block's message to an app-data metafield
  • actions/uninstall.ts
  • web/app/page.tsx: the embedded admin page, with App Bridge loaded in layout.tsx
  • extensions/storefront-block/: a theme block that reads that metafield
  • web/app/proxy/route.ts and proxy/status/route.ts: a Liquid page and a JSON endpoint on the storefront
  • web/proxy.ts: the frame-ancestors header Shopify requires on embedded pages (see Embedded admin pages)
  • tests/install.test.ts: a starter test of the install action
  • api/models/user/schema.ib.ts: email (required, unique), password, emailVerified, the confirmation and reset links' hashes and expiries, and sessions
  • api/models/user/actions/: signUp.ts, update.ts, sendVerifyEmail.ts, verifyEmail.ts, sendResetPassword.ts and resetPassword.ts
  • api/models/session/schema.ib.ts: belongs to a user
  • web/lib/auth.ts (sign-in, sessions, email confirmation and password reset) and web/lib/email.ts (Resend, with the app's own RESEND_API_KEY and EMAIL_FROM; until they're set, development's emails go to Logs as email events with their links, and production's aren't sent)
  • web/app/page.tsx, top-bar.tsx and footer.tsx
  • The auth pages: sign-in/, sign-up/, forgot-password/, reset-password/ and verify-email/, with auth-actions.ts, auth-form.tsx and email-forms.tsx
  • web/app/(signed-in)/: members-only pages, starting with account/
  • tests/signUp.test.ts: a starter test of sign-up
  • api/models/post/schema.ib.ts: title, slug (the address is /blog/<slug>), excerpt, body (Markdown), coverImage, published, publishedAt; actions/publish.ts puts a post on the site
  • api/models/lead/schema.ib.ts: name, email, company, message, source, ip, status; actions/submit.ts keeps only the form's fields, refuses more than five an hour from one IP and sets the status
  • api/models/subscriber/schema.ib.ts: email (unique), confirmed, the confirmation link's hash and expiry, a lasting unsubscribeToken; actions/subscribe.ts, renewConfirm.ts, confirm.ts and unsubscribe.ts
  • web/lib/site.ts (the name, description, About note, address and navigation; LEADS_TO for lead notifications), content.ts (the published posts, Markdown rendered with marked, reading time), forms.ts and email.ts (Resend, as the Members app's)
  • web/app/page.tsx (the latest post leads, then recent posts, the About note and the newsletter), blog/, blog/[slug]/, contact/, thanks/, confirm/ and unsubscribe/, with site-header.tsx, site-nav.tsx, site-footer.tsx and site-parts.tsx
  • lead-form.tsx, subscribe-form.tsx and site-actions.ts: the forms and the server actions they post to, each with a honeypot
  • sitemap.ts, robots.ts, feed.xml/route.ts (RSS) and opengraph-image.tsx: what search, link previews and feed readers see
  • tests/site.test.ts: a starter test of the lead limit, the confirmation link and publishing
  • One published post record, Hello, and how this site works at /blog/hello: a tour of the starter with its code, written as the app is created so the site looks like one from the start. Edit or delete it under Records → post.

Coding agents

The starter includes an AGENTS.md (with a CLAUDE.md that points Claude Code at it) and skills in .agents/skills/<name>/SKILL.md: models-and-schemas, actions, records-api, web-routes, security, testing and debugging, plus web-sign-in for a Members app, web-content for a Site, and shopify-admin-pages, shopify-storefront and shopify-data for Shopify apps. They are short guides that tell a coding agent (Claude Code, Codex, Cursor) how an Inference Bureau app is laid out and the practices to follow when it writes models, actions, admin pages and storefront blocks. They sync like any other file, so edit them to suit the app. An agent without a checkout connects to the environment's MCP server instead: development's has the app's files, models and actions as tools alongside its records, and tells the agent to read AGENTS.md and the matching skill before it changes code. Tests run only from a checkout.