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.
| Capability | Available? | 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:
idusernameemail(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 (useheaders.injectfor 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 packagebumps 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.
- In the Cribl UI, go to Apps > Create App from the top bar.
- On the Create your app screen, describe what you want to build, then select Next.
- Enter App ID, Display Name, Description, Author, Version, and Minimum Cribl version, then select Next.
- 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.
- 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
localStorageand must hold every API key and secret withencrypted=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.
- Start your local dev server by running
npm run devin your project directory. - 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.
- 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.
- Run
npm run packagein your terminal. - This compiles your App, auto-increments the patch version in
package.json, and produces a valid.tgzarchive. (You can override the version bump usingnpm run package -- --minor,npm run package -- --major, ornpm run package -- --version x.y.zfor 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
.tgzto 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.
From the App project directory, run the upgrade with the latest
@cribl/appsCLI:npx @cribl/apps@latest upgradeInstall the project’s dependencies:
npm installReview the changed files, then run
npm run devand test the App in Live Preview.
The upgrade can:
- Move an older App from its bundled packaging scripts to the
@cribl/appspackaging tools. - Add
config/policies.ymlwhen the project does not already contain that file. The upgrade does not overwrite an existingpolicies.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: 256The manifest supports these settings:
runtime: Required. The supported value isjs.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 is30, and the allowed range is1through900.memory: Optional memory limit in MB. The default is256, and the allowed range is1through1024.
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
.jsfile underbackend-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 functionnameinbackend.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 usescheduleId,scheduledFor,appId, andendpoint. 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 containsscheduleIdandscheduledFor.
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 fromproxies.ymlwith${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
hasHydratedflag 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: 30000Host 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 omitpaths, all paths on that hostname are allowed.paths.blocklist: The request path must not start with any listed prefix.blocklisttakes precedence overallowlist./: 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 and Deep Links
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 andproxies.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.
npmerrors or warnings: Confirm you’re on Node.js 22.22.2 or later (for example withnvm install 22.22.2andnvm use 22.22.2, or your version manager’s equivalent). Open a new terminal and retry thenpmcommand.- 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
cursororclaudeis on yourPATH. 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.mdand 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:
- Anthropic’s Prompt engineering overview and Prompting best practices are thorough reference material.
- Teresa Torres summarizes an end-to-end process for AI-assisted building in Vibe coding best practices. The article covers gotchas, testing, and debugging. Cribl does not maintain it. It’s an independent product-management perspective you can adapt alongside this guide.