# Apps

Deploy static sites, bundles, Dockerfiles, and OCI images to stable Kortix URLs.

Canonical page: https://app.teachervoiceai.com/docs/sdk/apps

Kortix Apps are provider-neutral serverless deployments. An App owns one stable URL. Each deployment is immutable. Failed deployments do not replace active traffic.

Apps is an experimental feature flag and off by default. The Apps navigation remains visible for a selected project and opens an enablement screen. Until a project manager enables **Apps** under Customize → Feature flags, every Apps route answers `403` with `{ error, code: "feature_disabled", feature: "apps" }`, and `kortix apps <subcommand>` prints that message and exits `1`.

All current source kinds run through the same Kortix sandbox hosting backend. Kortix selects Daytona, Platinum, or E2B. Static Apps, bundles, Dockerfiles, and OCI images therefore share one deployment and cold-wake contract. Cloudflare, Deno Deploy, Vercel, and other managed hosting backends are not part of the current API or SDK contract.

## Choose the fastest source path

- Deploy plain HTML, CSS, JavaScript, or a prebuilt SPA as `static`.
- Build Vite locally and deploy `dist/` as `static` for the lowest latency.
- Deploy Vite, React, or another package source as `bundle` when Kortix must run the install and build commands.
- Export Next.js with `output: 'export'` and deploy `out/` as `static` when the App needs no server runtime.
- Deploy server-rendered Next.js and arbitrary services with a Dockerfile and an explicit command and port.
- Deploy an existing public image as `oci_image` with an explicit command and port.

`kortix apps deploy` blocks by default until the stable URL is ready. Use `--no-wait` only when another process owns status tracking. An authorized request to a suspended App resumes its sandbox, waits for readiness, and proxies that same request. Browser navigation shows a branded page while the App is queued, validating, building, provisioning, checking, or starting. Machine clients receive `202 app_starting` with `Retry-After: 3` during a transient cold start. The stable URL does not expose `app_stopped` or an unavailable cold-start state.

New Apps are private. Choose one access mode:

- `private`: only the creator.
- `project`: every member who can read the project.
- `restricted`: selected project members and groups.
- `public`: no authentication.
- `password`: anyone with the App password.

Kortix access uses a five-minute exchange URL and an eight-hour, host-only, secure cookie. Changing an access policy increments its revision and revokes existing App cookies. Passwords are Argon2id hashes. The API and SDK never return a password or hash. Do not put a password in `kortix.yaml`.

Every immutable deployment records `created_by`, `actor_type`, and its originating `source_session_id` when an agent deployed it.

```ts
const apps = kortix.project(projectId).apps;
const app = await apps.create({ slug: 'docs', name: 'Docs' });
const artifact = await apps.artifacts.uploadArchive(tarGzBytes);
const deployment = await apps.deployments.create(app.app_id, {
  artifact_id: artifact.artifact_id,
  source: { kind: 'static', spa: true },
});
console.log(app.url, deployment.status);

await apps.access.update(app.app_id, {
  mode: 'restricted',
  member_ids: [memberId],
  group_ids: [groupId],
});
const preview = await apps.access.session(app.app_id);
window.open(preview.url);
```

For OCI images, register the immutable image reference and declare the process command and public target port:

```ts
const registered = await apps.artifacts.register({
  kind: 'oci_image',
  image: 'ghcr.io/acme/service:2026-08-07',
});
await apps.deployments.create(app.app_id, {
  artifact_id: registered.artifact.artifact_id,
  source: {
    kind: 'oci_image',
    image: 'ghcr.io/acme/service:2026-08-07',
    command: ['node', 'server.js'],
    port: 3000,
    readiness_path: '/health',
  },
});
```

Use `apps.access.get`, `apps.access.update`, `apps.access.session`, `apps.deployments.get`, `apps.deployments.logs`, `apps.rollback`, `apps.start`, `apps.stop`, and `apps.remove` for the full lifecycle. `apps.stop` suspends compute immediately. The next request resumes the App. `apps.start` warms it before traffic arrives.

`kortix apps access <app>` reads a policy. Add `--mode`, `--password`, `--members`, or `--groups` to update it. `kortix apps access-link <app> --json` creates a five-minute authenticated browser URL without changing the policy. Treat that URL as a secret. `kortix apps deploy` accepts the equivalent `--access`, `--password`, `--members`, and `--groups` flags.

Each cold start compares the active deployment's `runtime_version` with the current Kortix App runtime. An old deployment continues serving while Kortix asynchronously builds one immutable replacement with the latest `kortix-appd` and Caddy binaries. One PostgreSQL advisory lock prevents duplicate refreshes.

The Kortix Browser opens `*.apps.kortix.com` and `*.apps.localhost` directly. Direct navigation preserves the host-only access cookie and avoids the generic sandbox proxy.

Hosting provider selection remains a server policy unless an operator supplies the optional deployment preference. Managed acceptance covers Platinum and Daytona. E2B implements the same provider contract.
