Skip to content

Apps

Apps

An app is the Svelte page operators actually use. Apps are discovered from src/apps/**/+<lower_snake_case>.svelte , each filename is the canonical app ID, and each compiles into the client bundle with the signed-in user’s policy context.

Source layout

src/apps/
├── +operations.svelte
└── finance/
    ├── +group.ts
    └── +payroll.svelte

Group metadata is optional. A group directory owns the group ID; each app filename owns its app ID. No registration module is authored.

Static metadata

Each app declares literal metadata in <svelte:head> :

<svelte:head>
  <title>Operations</title>
  <meta name="description" content="Manage daily operations" />
  <meta name="bolt:icon" content="lucide:briefcase" />
  <meta name="bolt:thumbnail" content="https://cdn.example.com/operations-card.webp" />
  <meta name="bolt:banner" content="https://cdn.example.com/operations-banner.webp" />
</svelte:head>

The optional bolt:thumbnail image appears in the root app gallery. The optional bolt:banner image appears above the app and scrolls with the app surface. Both values must be static URLs.

Composition

The Bolt shell owns the app region and document scroll. Compose app bodies with PageHeader and the layout primitives: Stack , Inline , Cluster , Split , Grid , Columns , Cover , Center , and Frame . Every app follows the same body contract — one Cover , one body region, one scroll owner. See Layout for the full rules, which the compiler enforces.

Live data in apps

Reads in tenant apps go through the live data layer — client.db queries are live against a local PGlite replica, not one-off fetches. CollectionTable and custom views inherit that behavior: filters, sorts, and pagination stay reactive without cache invalidation in app code.

Client access

<script lang="ts">
  import { CollectionTable } from '@norbital-ai/ui/collection-table';
  import { Stack } from '@norbital-ai/ui/layout';
  import { PageHeader } from '@norbital-ai/ui/page-header';
</script>

<svelte:head>
  <title>Operations</title>
  <meta name="bolt:icon" content="lucide:briefcase" />
</svelte:head>

<Stack gap="lg">
  <PageHeader title="Operations" description="Manage daily operations." />
  <CollectionTable collection="sites">
    {#snippet columns({ Column })}
      <Column name="name" />
      <Column name="client_name" />
      <Column name="house_type" />
    {/snippet}
  </CollectionTable>
</Stack>

Every CollectionTable requires an explicit {#snippet columns({ Column })} — table UI does not auto-derive columns from the model. Schema-derived create and record forms are the default; one collection-owned +representation.svelte overrides them only when needed. See UI components .

Keep server-only behavior in hooks, pipelines, integrations, automations, or functions. App access is filtered by team policy, and collection operations use the same policy enforcement as other clients.