Build a web app that runs inside a Cortex instance — installed by an admin as a single zip, no hosting required.
React + Tailwind + Vite, pre-wired with the Cortex design language and a typed
client for the app hosting contract. Building with an AI coding agent? Point it
at the builder skill first: https://cortexskills.org/builder/app/SKILL.md.
npm install
cp .env.example .env # add your instance URL + a read key
npm run dev # build against LIVE data from your instance
…edit src/, describe your app to your coding agent…
npm run package # → {id}-{version}.zip
Upload the zip in your Cortex admin panel (Apps → Install) for a private app, or publish it to cortex-registry so any instance can install it.
The shipped workflow (.github/workflows/release.yml) does the whole thing on a tag push:
git tag v1.0.0 && git push --tags
# CI builds from the tagged source, validates, packages, publishes the# GitHub release with the zip attached and its sha256 in the notesThen, in a cortex-registry checkout:
node scripts/add-listing.mjs <the release asset url> --tags your,tags
# → writes apps/{id}/listing.json + rebuilds index.json; open a PRRegistry CI re-downloads the artifact and re-verifies the checksum. New
version = new tag + a listing-bump PR — never swap the asset under an
existing tag; the pinned digest is the trust anchor. (Manual flow, if you
prefer: npm run package, sha256sum, gh release create — same fields.)
- All requests are relative. Hosted apps are served under
/apps/{slug}/. Use the client insrc/lib/cortex.ts— never fetch an absolute/api/…URL.npm run validateenforces this. - No keys in the browser, ever. In production the hosting proxy attaches
your app's server-side scoped key; the browser only holds a short-lived app
token (handled for you by the client's postMessage handshake). In dev, the
Vite proxy plays that role with
CORTEX_DEV_KEYfrom.env. - Declare what you use.
app.jsonlists the Cortex endpoints your app may call (cortex.endpoints), the key scope, and any external hosts. The admin approves exactly that at install — undeclared calls are blocked by the proxy. - Self-contained bundle. Hosted apps run under a strict CSP
(
default-src 'self'). No CDN scripts, no external fonts — ship every asset indist/.
| Path | Purpose |
|---|---|
app.json | Your app's manifest — id, endpoints, scope, config vars (schema) |
src/lib/cortex.ts | Typed client: search(), askStream() (SSE), cortex(), platform() |
src/lib/platform.ts | Tasks + storage client: submitTask(), taskAction(), storageGet/Put(), … |
src/components/ | Demo panels exercising search + streaming Q&A with citations |
src/styles/index.css | Cortex design tokens (dark-first, sharp corners, mono labels) |
scripts/validate.mjs | Contract checks — run before every upload |
scripts/package.mjs | Builds {id}-{version}.zip in the installable format |
type: "platform" unlocks server-side capabilities declared under
capabilities. Shipped today:
http— external API calls executed by the instance with secrets injected from app config, so the target needs no CORS setup and credentials never reach the browser (prefer this over browser-directexternalHostsfor any authenticated service).tasks— declarative step-queues (http/cortex/llm/store/templatesteps) that Cortex runs server-side: work survives a closed tab, and aschedulemakes it recur with no browser at all. Client insrc/lib/platform.ts; DSL reference:cortexskills.org/builder/app/tasks.md.storage— the app's private, quota-capped key/value store.llm— completions via the instance's model inside task steps (metered).- Implicit config-read (
./api/platform/config, non-secret values only).
See the builder skill for the full guide. type: "service" is for apps that
need their own container; those ship compose templates instead of zips.
Dark-first, typography-led, sharp. Tokens live in src/styles/index.css;
the full spec is the cortex-design skill.
Instances can expose their own accent/logo via the platform branding
capability — prefer tokens over hardcoded colors so your app inherits it.
{ "id": "paperless-triage", // kebab-case, unique"type": "static", // static | platform | service"cortex": { "keyScope": "read", // read | read_write"endpoints": ["search", "ask"], // /api/ paths the proxy allows"collections": "user-selected" }, "config": [ // admin fills these at install; { "name": "PAPERLESS_TOKEN", // secrets are encrypted at rest and"type": "secret", // injected SERVER-side, never shipped"auth_header": "Authorization: Token PAPERLESS_TOKEN" } ], "externalHosts": [] // browser-direct hosts (CSP allowlist) }