Skip to content

Apps

Apps

Tenant apps are Svelte components discovered from src/apps/**/+<lower_snake_case>.svelte. Each filename is the canonical app ID. Apps compile into the client bundle and render 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="pod:icon" content="lucide:briefcase" />
  <meta name="pod:thumbnail" content="https://cdn.example.com/operations-card.webp" />
  <meta name="pod:banner" content="https://cdn.example.com/operations-banner.webp" />
</svelte:head>

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

Page scaffold

Apps compose a Page scaffold with optional Page.Tab children. Use fill on the page or tab when a collection view should bound viewport-remainder height (pair with pod:layout="contain" in the head).

Client access

<script lang="ts">
  import { collectionClient } from '$pod/client';
  import { Page } from '@norbital-ai/ui/page';
  import { CollectionTable } from '@norbital-ai/ui/collection-table';
</script>

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

<Page title="Operations" description="Manage daily operations." fill>
  <CollectionTable client={collectionClient} collectionName="sites" create>
    {#snippet columns({ Column })}
      <Column name="name" />
      <Column name="client_name" />
      <Column name="house_type" />
    {/snippet}
  </CollectionTable>
</Page>

Every CollectionTable requires an explicit {#snippet columns({ Column })} — table UI does not auto-derive columns from the model. Prefer create / createForm on the table for standard create flows.

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