Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.khal.ai/llms.txt

Use this file to discover all available pages before exploring further.

khal-app.json is the manifest every pack ships at its repo root. The platform reads it once at install time and from then on knows your pack’s identity, the permissions it requires, the frontend bundle to load, and the backend service (if any) to run. The schema is defined by @khal-os/types and validated with Zod, so mistakes are caught at build time rather than in production.

Required fields

Every pack manifest must declare these fields. The build will fail if any are missing or the wrong shape.
FieldTypeWhat it is
idstringStable identifier — lowercase, hyphen-separated. Used for NATS subjects, install paths, and log lines.
namestringHuman-readable name shown in the launcher and window title.
versionstringSemantic version of the pack (1.0.0). Bumped on every release.
iconstringRelative path to the pack icon (SVG recommended).
descriptionstringShort blurb shown in the launcher and the marketplace listing.
authorstringThe name or organization shipping the pack.
permissionsstring[]The capabilities the pack needs (nats:publish, files:read, etc.). Declare the minimum — the platform enforces the list at runtime.
Older manifests may include frontend.package. New generated apps are git-native: the install source is the repo/ref plus khal-app.json, and the platform build/install path resolves the frontend bundle from the repo instead of an app npm package.

Optional fields

These fields unlock additional pack shapes. You only declare what your pack needs.
FieldWhen to use it
frontendLegacy/shell frontend declaration. New app installs are git-native and should not require publishing an app npm package.
services[]Declare long-running processes your pack runs. Each entry names a process, its entry point, health check, and ports.
windows[]Default window sizes and titles for the shell. Override per-view dimensions here.
backendFor packs that ship a container image — names the image, Helm chart, env vars, and ports.
apps[]For bundle packs that ship multiple frontend apps in one repo. Each entry has its own id, name, and frontend declaration. When present, the root frontend is ignored.
sandboxFor packs that require a per-user container on install. Declares CPU, memory, and volume mounts.
Every optional field has a deep dive in the exhaustive khal-app.json schema reference — bookmark it when you start declaring services or sandboxes.

Worked examples

A minimum viable manifest for a frontend-only app. No backend, no services, one frontend surface.
khal-app.json
{
  "$schema": "https://raw.githubusercontent.com/khal-os/app-kit/dev/packages/types/src/khal-app-schema.json",
  "id": "<name>",
  "name": "<Name>",
  "version": "1.0.0",
  "icon": "./package/src/assets/icon.svg",
  "description": "A KhalOS pack",
  "author": "KhalOS Core Team",
  "permissions": [],
  "kind": "app",
  "requires": {
    "postgres": true,
    "nats": { "streams": ["<name>"] }
  }
}
Source: current khal new app scaffolds; exact optional fields may evolve with @khal-os/types.
Annotated khal-app.json with arrows from each field to the runtime component it configures — id to launcher, permissions to capability gate, app kind to shell loading, backend to the Kubernetes workload.

Permissions: declare the minimum

permissions is where you tell the platform what your pack intends to do. The platform enforces the list at runtime — a pack without nats:publish cannot publish to NATS, period.
Principle of least privilege. Start with an empty permissions array and add entries only when the build surfaces a denied capability. Packs that over-declare permissions are flagged in review.
Common permissions include nats:publish, nats:subscribe, files:read, files:write, pty:spawn, http:fetch, system:clipboard, and system:notifications. The full list and the semantics of each lives in the khal-app.json schema reference.

Secrets: declare, don’t embed

Never put secrets directly in khal-app.json. The manifest is committed to git and published as part of your pack. Instead, declare what your pack needs and let the platform provide the values at runtime — through the backend.env block (for non-sensitive config) or via the platform’s secret store (for credentials).

Validation

The toolchain validates your manifest on every build. If a required field is missing, a type is wrong, or a permission name is unrecognized, you’ll see a clear Zod error with the field path before the build proceeds. There’s no way to publish a pack with a malformed manifest.

Next steps

Full schema reference

Every field, every type, every enum value — the exhaustive reference for writing non-trivial manifests.

Anatomy of a pack

Step back out to the directory-level view and see where the manifest sits relative to everything else.