The reactive runtime for Cloudflare apps and their
agents
HighX is a TypeScript framework. You write plain typed functions.
HighX runs them next to your data, keeps every screen up to date
by itself, and gives your AI agents a safe place to work.
Every function is one of three kinds. Each kind says what it is
allowed to do — and the runtime enforces it, so mistakes fail loudly
instead of corrupting data quietly.
kind 1 / query
Reads data. Nothing else.
A query reads the database and remembers what it looked at. That
memory is what makes live updates work later.
When a mutation commits, HighX checks which queries touched that
data and pushes the new result to every subscriber. You never write
refetch code, cache keys, or polling loops.
—First result arrives with the page. No spinner wiring.
—New results arrive on their own. No polling.
—That is the whole client.
One tenant, one source of truth
Each tenant gets a private database — a Durable Object with SQLite
inside. Shared public data lives separately in D1. The two never
commit together, and HighX says so instead of hiding it.
Yours · the tenant database
Your tables, indexes, live queries, and agent runs.
Transactions that fully commit, or fully fail.
One tenant talks to one database. Simple to reason about.
Shared · the public read model
Public, read-only copies for fast pages.
Cached at the edge — it can be a little stale.
No live updates here in v1, and no pretending otherwise.
highx/signup.ts · work that spans
bothaction
// signup touches global and tenant data, so it is written// as steps — with a cleanup step if something fails.export const signUp = action({
args: signUpArgs,
handler: async (ctx, args) => {
const user = await ctx.run(createGlobalUser, args);
try {
const account = await ctx.run(createTenantAccount, {
userId: user.id, ...args,
});
await ctx.run(createSession, {
userId: user.id, accountId: account.id,
});
return account;
} catch (error) {
await ctx.run(markUserProvisioningFailed, { userId: user.id });
throw error;
}
},
});
the rules, in plain words
01Atomic means one database at a time.
02Work that spans both is written as steps, never hidden.
03Need signup to be all-or-nothing? Keep those records in one
place.
vecEmbeddings live in Vectorize; your table keeps the ID.
Being correct beats being clever. Every time.
HighX does not pretend that D1 and a
Durable Object can commit together.
AI agents with a leash and a ledger
An agent is a model plus a set of skills. A harness runs it in a
bounded loop. Every step is saved, every tool call is checked, and a
human can be put in the loop at any point.
the harness · a bounded loopwatch it run
The harness asks the model, calls one tool, looks at the result,
checks the budget — repeat. When the budget is spent, the run
stops. Agents cannot wander off.
what a run looks like
modelThe model decides the next step. Nothing more.
toolIt calls one declared tool. If it is not declared, it does
not exist.
observeThe tool result goes back to the model.
budgetSteps and tokens are counted. Out of budget — the run
stops.
savedEvery step is stored, so runs can sleep, retry, and resume
after a deploy.
Streaming token deltas are kept in a bounded replay buffer; the
durable record is steps, results, and interrupts.
A skill is a typed bundle: instructions, tools, input and output
schemas, and the capabilities it asks for. Versions are immutable
— a finished run replays against the same contract.
what a skill may actually dointersection only
A skill never grants power — it asks for it. It only gets what
every layer above also allows. Asking for more fails the build.
interrupts · a human in the loopexactly one winner
When an agent needs a person, it opens an interrupt — one row in
the tenant database. Many people can answer at once; the
transaction accepts exactly one.
The body is deterministic — no clocks, no randomness, no bare
fetch. Real work happens in named steps with stable IDs. Rename a
step, ship a new version.
We write down the limits too
Every tool promises the world. Here is exactly what HighX guarantees
— and what it openly does not.
Guaranteed
A write fully commits, or fully fails.
After a reconnect, live data always catches up.
Everything crossing a boundary is validated.
Pages load with data included — no second fetch.
An interrupt is answered exactly once.
A linked app cannot do more than it declared.
Not promised
No magic transactions across two databases.
No instant worldwide cache clearing — a cached page can be
stale.
No sandbox for code you link in. It is trusted code; review it.
No live updates on public D1 data in v1.
No compiler that can prove what arbitrary JavaScript does.
No automatic migration of running workflows after code changes.
A stale public page is acceptable; a logged-in live query
must catch up.
Everything maps to one platform
HighX does not hide Cloudflare behind fog. Each feature uses one
service, and its limit is written right next to it. If the platform
cannot do something, the build fails — it never silently degrades.
You need
HighX uses
The limit, in writing
tenant data
Durable Objects SQLite
One tenant = one place that can commit.
shared data
D1
Read-only in v1. No live updates.
live updates
Durable Object WebSocket
WebSocket is the required carrier.
files
R2
Big uploads use presigned URLs.
vectors
Vectorize
Indexes are created before first use.
background jobs
Workflows + Queues
Retries and idempotency are explicit.
models
AI Gateway
Usage is tracked in your own ledger.
secrets
Secret bindings
Never sent to the browser.
Take it for a run
Read the spec, then build the smallest thing: one query, one
mutation, one screen that updates itself. That is the whole idea —
everything else is the same pattern repeated.