pack-* repo later and know exactly where to look.
The tree
This is the layout you get when you scaffold from the template:
What each directory does
khal-app.json — the manifest
khal-app.json — the manifest
The declarative contract between your pack and the KhalOS platform. It names the pack, lists permissions, declares services and windows, and points the shell at the frontend package to load. The schema is enforced by
@khal-os/types at build time, so a broken manifest fails CI before it can be published.Every field — required and optional — lives in the khal-app.json reference.package/ — the frontend npm package
package/ — the frontend npm package
The React surface of your pack.
package/src/index.tsx default-exports a component; tsup.config.ts produces CJS + ESM + .d.ts bundles; package/package.json is what gets published to GitHub Packages as @khal-os/pack-<name>. The shell loads this package at runtime and mounts the default export into a window.service/ — the optional backend pod
service/ — the optional backend pod
The long-running process for packs that need one — a Bun HTTP server, a NATS subscriber, a background worker.
service/src/index.ts is the entry; Dockerfile is multi-stage; healthcheck.sh is a TCP probe used by Kubernetes. Delete the whole directory for frontend-only packs — CI will skip the Docker build automatically.helm/ — the service's Helm sub-chart
helm/ — the service's Helm sub-chart
A packaged Helm chart that deploys your
service/ image. Chart.yaml names the chart, values.yaml sets defaults (image repository, ports, resources), and templates/ renders the Kubernetes objects. You don’t hand-roll infrastructure — the chart is parameterized and the platform fills in the blanks..github/workflows/ — CI + release
.github/workflows/ — CI + release
Four workflows handle everything from PR validation to publishing. Every push or tag triggers the right one automatically:
Full release walk-through in Publish your pack.
| Workflow | Trigger | What it does |
|---|---|---|
ci.yml | Pull request | Lint, typecheck, build, test |
publish-npm.yml | Push to dev / main | Publishes @khal-os/pack-<name> to GitHub Packages (@next / @latest) |
docker-build.yml | Push to dev / main | Builds and pushes the service image (:next / :latest) |
helm-release.yml | Tag v* | Packages the chart and pushes it to oci://ghcr.io/khal-os/charts |
The contract in one sentence
You declare the shape; the platform provides the runtime. You write the manifest, the component, and (optionally) the service. The platform handles NATS connections, secrets, networking, installation, and deployment. That boundary is what keeps packs portable and the platform upgradeable.Next steps
khal-app.json reference
Every manifest field — required and optional — with examples pulled from real packs.
Publish your pack
The CI-driven path from commit to installable pack — branches, tags, and the four workflows that ship it.