On This Page

Home / Apps/Builder Guide: Designing and Building Apps

Builder Guide: Designing and Building Apps ​

Use this guide to design, build, and ship Apps. It covers how Apps run, how they store state, how they call APIs, and how you package and deploy them. It assumes you’ve completed the Quickstart and are ready to go deeper. Local development requires Node.js 22.22.2 or later, as described in Before You Begin in the Quickstart.

Browse published Apps in the Cribl Marketplace. For community App source and examples, see the Cribl Community GitHub organization.

App Structure ​

Before you start building, decide how much your App needs to do. Start small. A single-screen App that solves one problem well is more useful than a sprawling App that tries to do everything. You can always add views later.

Single-screen Apps: Use these for a focused dashboard, a simple form-driven workflow (for example, look up a value, trigger an action), or a single API interaction behind a purpose-built UI.

Multi-step Apps: Use these when you need to guide users through a sequential workflow, combine data from multiple Cribl APIs or external services, or maintain complex state across multiple views.

Runtime Model and Sandbox ​

The App UI runs as an embedded web application inside an isolated iframe in the Cribl UI. Treat the client-side App as an API client running in a constrained browser environment. Apps can also declare backend functions that Cribl runs server-side. For a security-review-oriented summary of these execution boundaries, see Runtime, Architecture, and Security.

  • The Cribl shell owns global navigation and chrome.
  • Your App UI runs in the iframe as a standalone Single Page Application (SPA).
  • All client-side reads and writes go through the documented Cribl APIs.
  • Persistent App state lives in the Apps KV store.

Iframe Sandbox Constraints ​

Because your App runs in an isolated iframe, certain standard browser capabilities are restricted to ensure security and Cribl UI stability.

CapabilityAvailable?Platform Alternative
fetch() to Cribl APIs✅Handled seamlessly by the Cribl fetch proxy.
localStorage / sessionStorage❌Not available in the sandbox. Use the KV store API.
IndexedDB❌Not available in the sandbox. Use the KV store API.
Cookies❌Not needed, auth is proxied automatically.
File downloads✅<a download>, Blob URLs.
Popups / new tabs✅window.open(), <a target="_blank">.
Canvas with same-origin images❌Fetch images through the proxy.

Signed-In User Identity ​

Cribl injects window.getCriblUser() into the App iframe at runtime. The function returns a memoized Promise that resolves to the signed-in member’s profile with the following fields:

  • id
  • username
  • email (optional)
  • firstName (optional)
  • lastName (optional)
  • initials (optional)

getCriblUser() works for installed Apps and for Live Preview. Call it once on startup. Repeated calls return the same resolved value.

const user = await window.getCriblUser();
// user.id, user.username, ...

Use the returned id when you need to distinguish members - for example, to include it in a KV key for per-member preferences. The KV store is not user-scoped by default. You choose how to namespace keys.

getCriblUser() provides identity only. It does not return roles or permissions. See Adapt the UI to the Signed-In Member.

The AI-First Developer Workflow ​

The fastest way to build with Apps is using AI coding assistants (like Cursor, GitHub Copilot, or Claude). Apps provides explicit guardrails to help your AI write Apps-compliant code, preventing it from hallucinating APIs or breaking sandbox constraints.

Expectations for Agent-Assisted Development ​

Agent-assisted (or “vibe-coded”) development is not the same as writing every line yourself. Assistants optimize for common web patterns and can still propose changes that break Apps rules, even when AGENTS.md is in the project. Plan to iterate: a first green build or successful preview is a checkpoint, not necessarily done.

Typical missteps to watch for include:

  • Using localStorage, sessionStorage, IndexedDB, or cookies instead of the KV store.
  • Calling Cribl URLs or HTTP methods that are not in the bundled OpenAPI specification, or inventing request shapes the proxy cannot satisfy.
  • Omitting an external hostname from proxies.yml, or adding headers in App code that Cribl strips (use headers.inject for secrets instead).
  • Refactoring UI or fetch code in a way that drops iframe-safe patterns that already worked in an older revision.

When output is wrong, tighten your guidance. Paste console or network errors. Say what you clicked or loaded. Quote the relevant bullets from AGENTS.md or this guide so the tool realigns to documented behavior.

For prompting habits that reduce thrash, see Further Reading on Prompting and AI-Assisted Delivery.

1. Create and Scaffold ​

Create App and Live Preview require a Workspace Administrator. See Roles, Permissions, and Governance in the Admin Guide.

First, define the App in the Cribl UI to generate your scaffold command. The metadata step stores values in the App manifest and shows them in the Apps inventory (for example, ID, Display name, Version, and Author).

  • App ID - Stable folder name and deep-link segment (/apps/a/<app-id>/...). Prefer a short slug (letters, digits, hyphens). Keep it fixed once others bookmark links or automation depends on the path.
  • Display Name - Human-readable title in the Apps list. Plain language is fine. It does not need to match the App ID.
  • Description - What the App does so admins and users can judge fit when browsing installed Apps.
  • Author - Person, team, or company that builds or maintains the App.
  • Version - Initial semantic version in package.json. npm run package bumps it when you ship (see 4. Package, Version, and Deploy).
  • Minimum Cribl version - Earliest Cribl release you wrote and tested against so admins can judge compatibility.
  1. In the Cribl UI, go to Apps > Create App from the top bar.
  2. On the Create your app screen, describe what you want to build, then select Next.
  3. Enter App ID, Display Name, Description, Author, Version, and Minimum Cribl version, then select Next.
  4. On the scaffold step, open a terminal and go to the parent folder where you want this App’s project folder created. Select Using Cursor, Using Claude, or Generic, then select Copy Script. Paste the CLI command into the terminal and run it.
  5. Leave the Create App wizard open in the browser through Live Preview. For the full sequence, see the Quickstart.

2. Initialize Your AI Assistant ​

The scaffold ships the platform rules your assistant must follow in AGENTS.md, which covers:

  • Authentication and the documented Cribl APIs.
  • The KV store, which replaces localStorage and must hold every API key and secret with encrypted=true.
  • Iframe and sandbox rules, along with the other platform constraints.
  • Capra, Cribl’s design system, as the UI stack for new work unless you direct the assistant to a different library or pattern. See Capra UI (Default).

Your assistant reaches those rules through the file it reads. Cursor reads AGENTS.md. Claude Code reads CLAUDE.md, which the scaffold points at AGENTS.md. Before you start iterative AI-assisted coding, confirm that Cursor has loaded AGENTS.md, or that Claude Code has loaded CLAUDE.md.

3. Develop With Live Preview ​

Iterate on your code locally while previewing it live inside the Cribl UI context. For Live Preview, use Chrome, Edge, or Firefox. See Browser Support.

The preview iframe loads your dev server from your machine (typically loopback). Browsers can block that path from an HTTPS Cribl tab until the user allows local network or local device access for the Cribl origin. If Live Preview is blank, confirm those permissions in the browser (and on macOS, under System Settings > Privacy & Security > Local Network for the browser). See Browser Permissions for Live Preview in the Quickstart.

  1. Start your local dev server by running npm run dev in your project directory.
  2. In the Cribl UI, on the Create App wizard step that shows the scaffold instructions, select Live Preview. The preview pane opens in that flow.
  3. The App loads in an iframe inside Cribl and hot-reloads instantly as you or your AI assistant make code changes.

Live Preview binds your local dev server to the Cribl UI session where you opened it. After you switch Workspaces, Organizations, or move between a local deployment and Cribl.Cloud, stop the server (Ctrl+C in the terminal). Run npm run dev again before the next Live Preview. If you skip that restart, the App can fail to initialize in the iframe.

4. Package, Version, and Deploy ​

When you are ready to distribute your App, do not manually tar or zip directories. Use the provided build tooling. The version line you set at create time seeds package.json and the packaged manifest. For metadata you enter at create time, see 1. Create and Scaffold.

  1. Run npm run package in your terminal.
  2. This compiles your App, auto-increments the patch version in package.json, and produces a valid .tgz archive. (You can override the version bump using npm run package -- --minor, npm run package -- --major, or npm run package -- --version x.y.z for an explicit semantic version.)

Deliver the Package to Your Administrator ​

The installable release is the .tgz from npm run package, not your Git repository. Keep source in Git for review and versioning. Give administrators the bundle or a URL they can import when members should run the App in a Workspace.

  • Import or upgrade (typical for staging and production): Send the .tgz to an Organization administrator. They use Add App > Import from File for a new App ID, or Upgrade on Installed for a new version of an existing App. Import again in each Workspace where members should run the App. Upgrade preserves Share settings. See Install an App and Development and Release Workflow for Customer Apps in the Admin Guide.

  • Deploy from Live Preview: In Live Preview, select Deploy to package and install in one step into the Workspace where you are previewing the App. See Step 6: Deploy in the Quick Start: Build and Install Your First App. Deploy suits fast iteration in a dev Workspace. An administrator may need to assign the App user access level after Deploy. See Share Access to Installed Apps in the Admin Guide.

Do not build or share a custom .tgz for Apps that Cribl publishes in the Cribl Marketplace. Administrators install those Apps from the catalog. See Install from the Cribl Marketplace in the Admin Guide.

To submit an App that you built for public listing in the Cribl Marketplace, see Publish an App to the Cribl Marketplace. After publication, you can also pursue the Cribl Certified badge.

5. Upgrade an Existing App Project ​

Upgrade an existing local App project to adopt the latest scaffold tooling and guidance without creating a new project or manually comparing scaffolds. The upgrade updates platform-managed files while preserving your App source and other developer-owned content.

Before upgrading, make sure the App project is under version control and commit all changes. The upgrade is designed to avoid breaking changes, but it cannot account for every project customization. A clean commit lets you review or revert the upgrade if needed.

  1. From the App project directory, run the upgrade with the latest @cribl/apps CLI:

    npx @cribl/apps@latest upgrade
  2. Install the project’s dependencies:

    npm install
  3. Review the changed files, then run npm run dev and test the App in Live Preview.

The upgrade can:

  • Move an older App from its bundled packaging scripts to the @cribl/apps packaging tools.
  • Add config/policies.yml when the project does not already contain that file. The upgrade does not overwrite an existing policies.yml.
  • Refresh the platform-managed section of AGENTS.md. Content outside the managed markers remains unchanged.

The command validates managed files before it applies versioned migrations. If your changes conflict with a migration, the command identifies the file and stops without applying any migrations. Resolve the conflict, then run npx @cribl/apps@latest upgrade again. You can safely rerun the command on an up-to-date project.

Backend Functions ​

Use backend functions when an App needs server-side logic, such as scheduled work or logic that should not run in the browser. Each function is an HTTP endpoint that runs on the Cribl platform. Every installable App requires static/index.html, but this file can be minimal when the App primarily uses backend functions.

New App scaffolds include a sample function. To build an App without backend functions, delete config/backend.yml and the backend/ directory.

Declare Backend Functions ​

Declare each function in config/backend.yml:

runtime: js
endpoints:
  - name: refreshLookup
    script: backend/refresh-lookup.ts
    description: Refreshes configured lookup sources.
    timeout: 45
    memory: 256

The manifest supports these settings:

  • runtime: Required. The supported value is js.
  • name: Required. A unique route segment for the function.
  • script: Required. A path to the function’s source file, relative to the project root.
  • description: Optional text that explains the function. This text does not affect execution.
  • timeout: Optional execution timeout in seconds. The default is 30, and the allowed range is 1 through 900.
  • memory: Optional memory limit in MB. The default is 256, and the allowed range is 1 through 1024.

Additional manifest properties do not change how Cribl routes or runs a function.

Write a Handler ​

Each source file must export an asynchronous onRequest function that accepts a web-standard Request and returns a Response:

export async function onRequest(request, context) {
  const response = await fetch('/api/v1/system/info');
  const info = await response.json();

  return new Response(JSON.stringify({
    appId: context.appId,
    info,
  }), {
    headers: { 'content-type': 'application/json' },
  });
}

The context argument includes:

  • appId: The installed App ID.
  • installationId: The stable installation ID.
  • invocationId: The ID for this invocation. Use it to correlate logs.
  • caller.userId: The opaque ID of the member who invoked the function. Use it for attribution, not authorization. The function does not inherit that member’s roles.

Keep work that requires configuration, network access, or secrets inside onRequest. The build process loads each module to verify its export, so module-level code runs during the build.

Build and Package Functions ​

Write handlers as ECMAScript modules. You can use TypeScript, npm dependencies, and relative imports. When you run npm run build, apps build:

  • Type-checks files under backend/.
  • Bundles each function and its dependencies into one CommonJS .js file under backend-build/.
  • Leaves node:* built-in module imports external to the bundle.
  • Confirms that each bundle exports onRequest.
  • Rejects a bundle larger than 5 MB.

Do not edit or commit backend-build/. The packaging command adds the generated bundles to the .tgz archive and updates the packaged backend.yml paths. Cribl does not install npm dependencies or resolve relative imports when it deploys the package, so always use the scaffolded build and package commands.

During import, Cribl also verifies that each declared script exists, does not exceed 5 MB, and uses a path that stays inside the App package.

Call a Backend Function ​

From the App UI, call the function through the App API base URL. Cribl automatically scopes the request to the current App:

const response = await fetch(
  `${window.CRIBL_API_URL}/endpoints/refreshLookup`,
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ source: 'daily-feed' }),
  },
);

The deployed route is /api/v1/a/<app-id>/endpoints/<name>.

Request bodies can contain up to 4 MB, and response bodies can contain up to 6 MB. Cribl rejects a request that exceeds its limit and returns an error when a function produces an oversized response.

Cribl authorizes backend function calls for the installed App, not as the member who invoked the function:

  • Declare required Cribl API paths in config/policies.yml. These grants apply to every backend function in the App.
  • Declare external hosts in config/proxies.yml. Backend functions also receive access to the App’s own KV store and proxy paths.
  • Use relative URLs with fetch() for Cribl APIs and full HTTPS URLs for external APIs.

Do not declare permissions in backend.yml. The declarations in policies.yml and proxies.yml apply to the entire App.

Schedule a Backend Function ​

To run a function on a schedule, declare the schedule in config/schedules.yml, not in backend.yml. Scheduled functions can run when no one has the App open. At fire time, Cribl sends a POST request to the same onRequest handler used for HTTP calls. Do not create a separate onSchedule handler. Read scheduledFor from the JSON body.

refresh-hourly:
  endpoint: refreshLookup
  cronSchedule: '0 * * * *'
  # bodyExpression: '{ scheduleId, scheduledFor, source: "daily-feed" }'

Each record supports these settings:

  • Top-level key: Required. The schedule ID. By default, an App can declare up to 25 schedules. An administrator can set the limit as high as 100.
  • endpoint: Required. Must match a function name in backend.yml.
  • cronSchedule: Required. A five-field UTC cron expression. There is no seconds field and no timezone.
  • bodyExpression: Optional JavaScript that creates the request body at fire time. It can use scheduleId, scheduledFor, appId, and endpoint. The default maximum length is 4,096 characters. An administrator can set the maximum as high as 16,384 characters. If you omit it, the body contains scheduleId and scheduledFor.

A schedule runs when it is present in the package. There is no enable or disable field. To stop a package-declared schedule, ship a new package that omits it, then have an administrator upgrade the App.

An App can also create, update, or delete schedules dynamically through the App-scoped Schedule API. Grant only the methods the App needs in config/policies.yml: use /backend-schedules for GET and POST, and /backend-schedules/* for GET, PATCH, and DELETE. A package upgrade reconciles the installed schedules with config/schedules.yml and can overwrite dynamically changed package schedules or remove dynamic schedules that are absent from the package.

After installation, administrators can inspect these declarations on App Settings > Schedules. See Schedules Tab in the Admin Guide.

Log From a Backend Function ​

Use console.debug, console.log, console.info, console.warn, or console.error in a handler. Cribl writes the output to app-backend.log and adds the App ID, function name, and invocation ID. Do not log secrets or raw credentials.

Log delivery is best effort. A timeout, out-of-memory failure, or hard crash can stop the function before Cribl flushes its buffered log entries.

Administrators can use the App ID and invocation ID to find a function’s logs. See Monitoring, Logging, and Auditing.

State and Storage ​

The iframe runs in a sandboxed context without allow-same-origin. Do not use localStorage, sessionStorage, IndexedDB, or cookies. Doing so can cause your App to fail at runtime. Apps provides a KV store as your only persistent store.

The KV Store ​

Each App has its own key-value store for persisting state, configuration, and secrets. Apps isolates KV data per App, so each request from your App code applies only to that App’s keys.

REST shape: From App code, call fetch() with relative URLs /api/v1/kvstore/<key>. Replace <key> with any string your App owns (for example settings or user-preferences). PUT sends the raw body as the value. GET returns the stored string. Values are opaque to Cribl, so you typically stringify structured data before you write it and parse it after you read.

Values: A value can be any string you can send in the request body, not only small structured settings. Cribl treats the payload as opaque bytes for storage. In practice, Apps persist everything from short flags to large serialized documents, including multi-megabyte artifacts. If you store large values, load them asynchronously and keep the UI responsive. See Async hydration patterns.

Quota: Each App can store up to 1,000 keys by default. To raise the limit, contact Cribl Support.

API keys and credentials: Always write API keys, bearer tokens, passwords, and any other secret material to KV with encrypted=true. Plain (unencrypted) KV values are visible to anyone in your Organization who can use the KV APIs for that App context, not only to members who open your App UI. Encryption is required for secrets and is the supported way to reference them from proxies.yml with ${kv...} expressions resolved on the server.

Secrets: On PUT, append encrypted=true for every credential. Encrypted entries are write-only from the client. Reads return a redacted placeholder. Do not store secrets without encryption.

Key-Value Stores UI: Administrators can pre-seed, edit, clone, or delete keys under App Settings > Key-Value Stores. Clone is available only for unencrypted entries. The External API Access tab in App Settings lists the external hosts the App may call, and administrators can authorize additional hosts there. Organization-wide policy is described in External API Access in the Admin Guide. Keys you create in Key-Value Stores share the same namespace as /api/v1/kvstore/... calls from App code. In the UI, add API keys and tokens only as encrypted entries.

Example: Write and Read settings From App Code ​

This snippet is ordinary App JavaScript (for example inside a React effect or handler). It writes a stringified configuration object under the key settings, then reads that key back.

// Write a setting (values are opaque strings; use JSON for structured data)
const settingsPayload = JSON.stringify({ theme: 'dark', refreshInterval: 30 });
await fetch('/api/v1/kvstore/settings', {
  method: 'PUT',
  headers: { 'Content-Type': 'text/plain;charset=UTF-8' },
  body: settingsPayload,
});

// Read it back
const resp = await fetch('/api/v1/kvstore/settings');
const raw = await resp.text();
const settings = JSON.parse(raw);

Async Hydration Patterns ​

Because KV access is async, plan for “no data yet” on initial render:

  • Loading gate: Wrap your root component in a loader that fetches required KV keys before rendering the main App.
  • Write-after-hydrate: Track a hasHydrated flag to prevent default local state from overwriting stored KV values during the initial load.

API Calls and Data Flow ​

Platform APIs from the App UI ​

To call Cribl REST APIs from the App UI, use standard fetch() with relative URLs. Cribl intercepts the request, injects the signed-in member’s auth headers, and routes it. Only call documented endpoints from the OpenAPI specification and API Reference. Calls from backend functions use the App’s grants instead; see Call a Backend Function.

External APIs and proxies.yml ​

To call external services, use standard fetch() with the full URL. Cribl intercepts the request and routes it through a server-side proxy.

Declare every external domain your App always needs in a proxies.yml file at your project root. Cribl enforces this declaration at runtime, ensuring administrators know exactly what external endpoints your App communicates with.

For domains that vary by customer, such as an identity provider tenant, omit those domains from proxies.yml. An administrator authorizes them after install on App Settings > External API Access, so you do not package a release per customer. Your App cannot widen its own access. See External API Access Tab in the Admin Guide.

api.openai.com:
  paths:
    allowlist:
      - /v1/responses
  headers:
    inject:
      Authorization: '`Bearer ${kv.api_key}`'
  timeout: 30000

Host and Path Matching ​

Top-level keys in proxies.yml are hostnames. Each key must be an exact hostname. Wildcards are not supported for hostnames. If a service uses multiple hostnames, declare each one separately.

paths controls which URL paths are allowed on a declared hostname:

  • paths.allowlist: The request path must start with at least one listed prefix. If you omit paths, all paths on that hostname are allowed.
  • paths.blocklist: The request path must not start with any listed prefix. blocklist takes precedence over allowlist.
  • /: Allows all paths on that hostname.
  • A path prefix such as /v2/: Allows any path that starts with that prefix (for example /v2/data/report.json).

headers.allowlist and headers.blocklist support * glob patterns for header names (for example x-amz-*). That is separate from hostname and path rules.

The following example declares three hostnames for the same service and allows all paths on each host. Use the same paths rules on every hostname you add.

api1.example.com:
  paths:
    allowlist:
      - /
api2.example.com:
  paths:
    allowlist:
      - /
api3.example.com:
  paths:
    allowlist:
      - /

To allow only a specific API version on one host, use a narrower prefix:

api1.example.com:
  paths:
    allowlist:
      - /v2/

Sensitive headers set by App code are always stripped from outgoing requests for security. Use headers.inject in your proxies.yml to securely add credentials stored in the KV store.

Routing runs entirely inside your iframe using a client-side router (React Router, Vue Router, and so on) that integrates with window.history.

To allow users to bookmark specific views or share them with teammates, support deep linking by encoding minimal state into the URL.

In Safari, client-side routing in the iframe behaves differently than in Chrome, Edge, or Firefox. See Browser Support.

When a user opens a deep link, the host route uses this pattern:

/apps/a/<app-id>/<path-in-app>

For example: /apps/a/my-app/settings/general?tab=advanced.

Cribl restores the path segment below /apps/a/<app-id>/, plus the query string and hash, inside the iframe on load. Your App’s router reads that URL and renders the correct view.

Permissions and Access ​

Who can create Apps, install packages, launch installed Apps, and assign App user sharing is described in Roles, Permissions, and Governance in the Admin Guide.

Two layers apply to the App UI: the App user grant controls whether a member may open a given installed App, use its App-scoped KV and proxy paths, and invoke its backend functions. Stream, Edge, Search, and Lake roles still gate general Cribl API calls from the App UI. Test with accounts that match both layers.

For the backend authorization model, see Call a Backend Function.

Adapt the UI to the Signed-In Member ​

The App UI runs as the signed-in member. You can tailor views and actions to that member instead of showing the same experience to everyone.

  • Identity: Use getCriblUser() for who is signed in (for example, display name or per-member KV keys).
  • Permissions: Through the OpenAPI specification and API Reference, you can read the signed-in member’s roles and effective policy - the same authorization context the main Cribl UI uses to show or hide options. These calls always reflect the current session. You do not pass a user ID, and you cannot query another member’s access.
  • Adaptive UI: Use that context to show or hide views, disable unavailable actions, and store per-member settings in KV (for example, keys that include the member’s id). Permission introspection tells you what to offer. Product RBAC still enforces each App UI API call. Handle 403 responses explicitly when a member attempts an action they cannot perform.

Declare any product API paths your App calls in policies.yml so administrators can review them at install time. See Declare In-Product API Permissions Your App Needs.

Declare In-Product API Permissions Your App Needs ​

Ship policy metadata in your bundle when the App UI or backend functions call Cribl REST endpoints. The authoring template provides config/policies.yml. Keep its declarations limited to the API paths and methods the App needs, so administrators can review them during Add App > Import and Upgrade.

For App UI calls, the signed-in member’s product roles still apply. If you omit a required declaration, members with narrower roles might see empty lists, disabled actions, or HTTP 403 responses. For backend calls, policies.yml grants apply App-wide to every declared function instead of inheriting the invoking member’s roles. Without a required grant, that backend API call is denied. The App’s own KV and proxy paths are available implicitly.

Keep declarations minimal and aligned to real UI flows. See Roles, Permissions, and Governance and App User Grants and Product APIs in the Admin Guide for how review and enforcement interact.

  • Handle 403 responses explicitly: catch 403 status codes and display a helpful message explaining that the signed-in member lacks required access. Do not let a single failing API call render an empty screen for the entire App.
  • Dim or disable unavailable actions: if you can determine permissions via a preflight call, disable buttons rather than letting members hit a wall.
  • Document required roles: list the permissions your App requires in your App’s README or first-run screen.

Logging and Troubleshooting ​

  • Logging: Log significant events (API failures, destructive actions) with context, but do not log secrets or raw credentials.
  • DevTools: Use your browser’s DevTools to inspect network requests and console output. Attach DevTools to the iframe context, not the top-level Cribl shell.
  • Debug with your AI assistant: Copy failing console messages, stack traces, or redacted network error bodies from the iframe context. Paste them into your coding assistant with a short description of what you were doing. Because the assistant already has your repository, it can often propose a fix quickly. It does not run inside Cribl, so it may guess about Apps behavior. When a suggestion looks off, ask it to reconcile the change with AGENTS.md, the OpenAPI specification and API Reference, and the Runtime Model and Sandbox and External APIs and proxies.yml.
  • Stale preview: If you change your build tooling configuration, perform a full browser reload of the Cribl tab to re-establish the Live Preview connection.
  • Preview after switching context: If you move between Workspaces, Organizations, or local and cloud deployments while using Live Preview, restart the local development server (npm run dev) so the App can initialize cleanly in the new context.
  • Server logs: Cribl records structured access for proxied traffic. Work with your Cribl administrator to correlate server-side logs with your App if you need request-level forensics.
  • npm errors or warnings: Confirm you’re on Node.js 22.22.2 or later (for example with nvm install 22.22.2 and nvm use 22.22.2, or your version manager’s equivalent). Open a new terminal and retry the npm command.
  • The scaffold does not open in Cursor or Claude Code: The CLI can launch Cursor or Claude Code when you use the corresponding command from the wizard. If the editor does not open, the scaffold remains in the project folder. Open it manually. Confirm that cursor or claude is on your PATH. For Claude Code, use an interactive terminal.

Capra UI (Default) ​

Capra is Cribl’s design system for embedded Apps. New scaffolds install Capra from the public npm registry and configure the design system by default, including a sample UI and AGENTS.md guidance for coding assistants. Capra provides the default design system, while still giving teams the flexibility to use or layer in their own UI stack when needed.

The design system and Capra’s React components are separate choices. You can use Capra design tokens and styling with your own components, keep Capra components for UI that matches Cribl accessibility and UX patterns, or replace Capra with another UI library entirely. See UX Guidelines when you change either layer.

For installation, component APIs, design guidance, and token usage, see Capra documentation. For AI-assisted development, point your coding assistant at capra.cribl.io/llms.txt, an AI-readable index of that documentation. Your scaffolded AGENTS.md and CLAUDE.md both reference the index and tell the assistant to load the Page Templates it lists before writing UI.

UX Guidelines ​

  • Default to Capra: For new Apps, use Capra components and design tokens so your UI stays aligned with Cribl accessibility and UX patterns. See Capra UI (Default).
  • Bring your own UI when needed: You can build with your own components while keeping Capra tokens and styling, or replace Capra entirely. You still own layout, behavior, and styling. Follow iframe rules in AGENTS.md and the Runtime Model and Sandbox, and tell your assistant clearly when you are not using Capra components so it does not reintroduce Capra-only assumptions.
  • Make primary actions obvious: Treat destructive actions explicitly with confirmation dialogs and clear danger styling.
  • Write clear error messages: Tell the member exactly what failed, whether they can fix it themselves, and when they need to talk to an administrator.

Further Reading on Prompting and AI-Assisted Delivery ​

These resources are not specific to Cribl, but they match how teams iterate with coding assistants: