SWIRLS_
Interfaces

Apps

Declare the bespoke Surface people use to work with your Swirls system.

What it is. An App is the hosted interface for the people who use a Swirls system. It can be an internal operations desk, a branded client portal, or a review workspace. You declare the shell, navigation, and pages beside the workflows, data, access, and agents that power them; Swirls validates and deploys the whole project together.

Use it when your team or your client needs an exact-fit workspace—not the builder dashboard. Use a page shortcut when the job is a shared agent room or review inbox. Use render: @openui { … } when the job needs a bespoke dashboard, data browser, or workflow-launching surface.

Where Apps fit

An App is the ongoing interface to the system: the place people return to see operational state, start workflows, work with agents, and make reviewed decisions. It brings the primitives behind a Swirls project into one exact-fit piece of software.

Not every interaction needs an App. Use a form for a focused human submission, a webhook when another system sends an event, and a schedule when work starts at a particular time. Those resources initiate workflows. An App gives the people responsible for the operation a place to keep using what you built.

Apps are optional. A workflow-only or agent-only project is complete when that is all the job requires; add an App when people need a durable interface around the work.

Start with an app block

An app needs a bare identifier, a label, and at least one page. Pages have exactly one body: an OpenUI render tree, a shared portal room, a thread directory, or a workflow review inbox.

app support_desk {
  label: "Support Desk"
  description: "Triage, approvals, and the support room in one place."
  icon: "headset"

  shell {
    preset: sidebar
    topbar {
      right: [widget.user_menu]
    }
  }

  nav {
    section "Work" {
      page: queue
      page: approvals
      page: room
    }
  }

  page queue {
    label: "Ticket queue"
    render: @openui {
      root = ViewBrowser("open_tickets", { title: "Open tickets" })
    }
  }

  page approvals {
    label: "Approvals"
    icon: "inbox"
    reviews: [refund_request]
  }

  page room {
    label: "Support room"
    icon: "hash"
    channel: support_room
  }
}

The support_room channel must declare platform: portal. The refund_request workflow and open_tickets view must exist somewhere in the same Swirls workspace.

Choose the right page body

BodyUse it forWhat people get
render: @openui { … }A dashboard, launcher, browser, or custom operational pageA compile-time validated composition of OpenUI components bound to Swirls primitives
channel: <channel>Shared work with an agentA full-viewport room for a platform: portal channel
threads: [<channel>, …]Find and resume conversationsA full-viewport directory for one or more platform: portal channels
reviews: [<workflow>, …]Human decisions that pause workflowsA full-viewport inbox for pending, approved, and rejected reviews

OpenUI is authored when you write the app, not generated at deploy or run time. The layout in Git is the layout people receive. Use components such as ViewBrowser, StreamBrowser, TableEditor, WorkflowLauncher, RunTimeline, Chat, and StatCard to compose the page your operation needs.

page launch_refund {
  label: "Start a refund"
  render: @openui {
    root = WorkflowLauncher("refund_request", [
      {
        name: "ticketId",
        label: "Ticket",
        required: true,
        input: Select({ view: "open_tickets" })
      }
    ], "Request refund", "Refund request")
  }
}

Set viewport: full on an OpenUI page when the artifact should own the main pane. Channel, thread-directory, and review pages are always full viewport.

Make it feel like the business that uses it

Use brand to set a logo, accent, avatar treatment, and a sparse light/dark token overlay. Apps inherit the Swirls palette for any token you do not set.

app client_portal {
  label: "Acme Client Portal"

  brand {
    logo: "https://acme.example/logo.svg"
    accent: "#B33A2B"
    tokens {
      light {
        canvas: "#f7f5f1"
        viewport: "#ffffff"
        brand: "#B33A2B"
        brand_foreground: "#ffffff"
      }
    }
  }

  page home {
    render: @openui {
      root = Stack([StatCard("Open requests", "12")])
    }
  }
}

An optional landing block customizes the sign-in experience for deep links. Its discover sub-block can opt the landing page into search indexing and publish safe app metadata for people and agents.

Control who can use it

Without audience, an App is for organization members. Add an audience block to declare an external client or partner admission policy; Swirls hosts audience-enabled apps on a stable *.swirls.cloud URL while Cloud manages invitations, sessions, and identity-provider credentials.

audience {
  admission: invite_or_domain
  auth: email_otp
  allow_domains: ["acme.com"]
}

The DSL owns the versioned policy. Cloud owns operational access such as invitations, user sessions, and IdP client secrets.

The page is also the permission boundary

An App can reach only the primitives it names. When Swirls resolves the app, it derives an expose set from the agents, channels, workflows, views, streams, databases, and queries referenced by its OpenUI pages, room pages, thread directories, and review pages. Surface uses that server-enforced set to scope data and execution access.

Naming a workflow in a reviews: page additionally grants review authority for that workflow in that exact app. Referencing a workflow from an OpenUI component alone does not grant review authority.

Next steps

On this page