HighX
Platform

Linked apps

Another HighX application installed inside yours, contained by name and capability.

A linked app is source in your repository, built with yours and served under a mount. Declare each one in highx.config.ts, keyed by the name its manifest gives:

export default defineConfig({
  apps: {
    billing: { source: "./apps/billing", mount: "/billing", grant: ["net:api.stripe.com"] },
  },
});

highx generate reads each highx.app.json — parsed, never evaluated — and refuses to write a deployment when a check fails. Configuration is the reviewable half; the host still installs the app in code, because the tenant projection is a function.

An inline app is not a sandbox. Its source runs in your isolate with whatever your imports can reach. Namespacing, grants, and the tenant projection hold, but there is no process boundary and none is claimed. Review an app's source as your own.

What is contained

Every table, queue, schedule, and route an app owns carries its namespace, and the prefix is applied by the runtime rather than accepted from the caller — a name an app assembles cannot resolve to yours. An app's manifest declares requirements; the installation grants. Declaring exec:arbitrary gets an app nothing.

The app receives ctx.db scoped to its declared tables, a projected request with your credentials stripped, a tenant it did not choose, and a ctx.fetch restricted to the hosts it declared. Its response passes an allowlist on the way out, and a relative redirect gets the mount put back on.

Build errors

CodeWhen
mount_collisionTwo mounts overlap. /billing and /billing/invoices collide; /billing and /billingx do not
table_collisionTwo apps claim one namespaced table
ungranted_capabilityA manifest requires what its installation does not grant
cycle, unknown_dependencyApps depend on each other in a loop, or on one that is not installed
escapes_appAn import resolves outside the app's own directory
forbidden_importThe app imports the runtime, a platform adapter, or a node: builtin
ambient_globalThe app calls fetch bare rather than through ctx.fetch

The last three read imports and call expressions, not the full semantics of JavaScript, so a determined author can still defeat them. They raise the cost of an accident from zero to a build failure, which is the honest claim.

Route permissions

An app declares what a route needs; you decide who holds it. Permissions are evaluated host-side against your table — an app never sees roles.

{ "name": "billing", "routeAuth": { "/invoices/:id": "billing:read" } }

Each must start with the app's own name, so an app gates on its own vocabulary rather than reaching into yours.

The manifest is one document

The installer reads highx.app.json; the app's module imports it. They must be the same document — the host grants against one and the runtime enforces routeAuth and the egress allowlist from the other, so a drift between them would mean the copy that was reviewed is not the copy that runs. A disagreement is refused at startup.

isolate: "worker" exists in the type and install refuses it: nothing deploys or routes to an isolated app yet. The app-facing API is shaped so isolation can be added without rewriting apps.

On this page