Skip to content

Quick Start

Quick Start

In a few minutes you will have a small but complete workspace: one model, its collection, and one app with one page, checked and built. The same source runs locally with bolt dev, on your own server with bolt start, and on Colony in Workspace Studio.

tasks/
├── package.json
├── tsconfig.json
└── src/
    ├── +workspace.ts
    ├── data/
    │   ├── model/+tasks.model.ts
    │   └── collection/tasks/+collection.ts
    └── app/tasks/
        ├── +app.ts
        └── +board.page.svelte

1. Create the package

{
  "name": "tasks",
  "private": true,
  "type": "module",
  "scripts": { "check": "bolt check", "build": "bolt build", "dev": "bolt dev", "test": "bolt test" },
  "dependencies": {
    "@norbital-ai/bolt": "0.0.1",
    "@norbital-ai/ui": "0.0.1",
    "effect": "4.0.0-rc.112",
    "svelte": "^5.57.1",
    "vite": "^8.2.2"
  },
  "devDependencies": {
    "@norbital-ai/bolt-server": "0.0.1",
    "@effect/vitest": "4.0.0-rc.112",
    "@sveltejs/vite-plugin-svelte": "^7.3.1",
    "svelte-check": "^4.7.6",
    "typescript": "6.0.3",
    "vitest": "4.1.11"
  }
}
{ "extends": "./.norbital/tsconfig.json" }

A workspace is a package that depends on @norbital-ai/bolt and @norbital-ai/ui. Its tsconfig.json extends the generated .norbital/tsconfig.json. There is no vite.config.ts and no SvelteKit config: the compiler owns the build.

2. Declare the workspace

// src/+workspace.ts
import { workspace } from '@norbital-ai/bolt';

export default workspace({ tz: 'Asia/Singapore', locale: 'en-SG', apps: ['tasks'] });

src/+workspace.ts is the one required file. It sets the zone and the locale every date and number is read in, and the order of the apps in the launcher.

3. Define a model

// src/data/model/+tasks.model.ts
import { model } from '@norbital-ai/bolt';

export default model({
  description: 'Work the team has to do.',
  icon: 'lucide:list-todo',
  label: 'title',
  fields: {
    title: { kind: 'text' },
    status: {
      kind: 'state',
      initial: 'open',
      states: { open: { to: ['done'] }, done: { to: ['open'], edit: 'none' } }
    },
    due_on: { kind: 'date', optional: true }
  },
  search: { text: ['title'] }
});

A model is storage. The folder name tasks is the model’s name; fields are typed literals, label is the record title, and a state field is the only lock: each state lists where it may move and what may still be edited in it.

4. Expose it as a collection

// src/data/collection/tasks/+collection.ts
import { collection } from '@norbital-ai/bolt';

export default collection('tasks', {
  read: { fields: 'all' },
  create: { input: { columns: ['title', 'due_on'] } },
  update: { input: { columns: ['title', 'status', 'due_on'] } },
  delete: {}
});

A model with no collection is invisible. The collection says what any caller can ever read (read.fields) and which fields a create and an update accept. Declaring delete exposes deletes; an operation left out does not exist.

5. Add an app and a page

// src/app/tasks/+app.ts
import { app } from '@norbital-ai/bolt';

export default app('tasks', {
  title: 'Tasks',
  description: 'Everything the team is working on.',
  icon: 'lucide:list-todo',
  pages: { board: { title: 'Board', icon: 'lucide:kanban' } }
});

+app.ts names the app and lists its pages in nav order; every +<page>.page.svelte in the folder needs an entry. Titles here are literals; once the workspace has src/i18n/, they become catalog keys.

<!-- src/app/tasks/+board.page.svelte -->
<script lang="ts">
  import { Board } from '@norbital-ai/ui';
  import { AppShell } from '@norbital-ai/ui/layout';
</script>

<AppShell icon="lucide:list-todo" title="Tasks" description="Everything the team is working on.">
  <Board of="tasks" by="status" card={['title', 'due_on']} />
</AppShell>

A page is a Svelte component. Board of="tasks" reads the collection live, groups it by the state field, and offers New and the moves each viewer may take.

6. Check, run and build

pnpm install
pnpm exec bolt check   # layout, names, schema, tsc, svelte-check and doctor, every error at once
pnpm exec bolt dev     # the workspace on a local PGlite, reloading on save
pnpm exec bolt build   # .norbital/artifact/: the one artifact every host runs

bolt check runs every stage — layout, names, bundle, schema, tsc, svelte-check and the doctor — and reports every error at once; there are no warnings. bolt dev serves the workspace on a local PGlite database and reloads on save. bolt build writes the artifact every host runs.

Who can see it
The member who founds a workspace is an administrator and bypasses grants. Everyone else sees an app only when one of their policies names it in capabilities.apps, and a collection only through its grants: see Policies.

Next

You now have a buildable workspace. From here: