Build with Dehook

A function in your browser. The same function from local JavaScript. One backend behind both.

GETTING STARTED

Start with your app

Dehook gives you a backend by exporting ordinary JavaScript functions. The browser and local JavaScript clients share the same app, token pair, functions, and document store.

Your active public token is loading. Use the workspace selector to switch backends. Open Settings for credentials or to create a new backend.

Development HTML
Preparing your snippet

Dehook is already loaded on this page. Open your browser console to try the examples directly.

Export and run functions

Export a named function once. It stays on your VPS until you overwrite it. Call it by name from either client.

JavaScript
dehook.export(function hello(data) {
  return { response: `Hello ${data.name}!` };
});

dehook.run('hello', { name: 'Ada' }, (response, error) => {
  if (error) return console.error(error.message);
  console.log(JSON.parse(response));
});

export() returns { processorURI }. Calls are sent in order, so a run placed after an export waits for it. Callbacks receive the JSON response as a string and an optional error.

List functions and retrieve source
dehook.exports((functions, error) => {
  if (error) return console.error(error.message);
  console.log(Object.keys(functions));
});

dehook.import('hello', (source, error) => {
  if (error) return console.error(error.message);
  console.log(source);
});
Exported functions run on the server. They cannot close over local variables or import your app's packages. Functions must return synchronously.

Store and query documents

Each token pair has its own persistent JSON document store. Direct client queries require the private token.

JavaScript
dehook.query({ put: { type: 'note', text: 'Hello Dehook', done: false } });

dehook.query({ search: { type: 'note' } }, (response, error) => {
  if (error) return console.error(error.message);
  console.log(JSON.parse(response));
});

dehook.query({ update: { type: 'note' } }, { done: true });
dehook.query({ remove: { type: 'note', done: true } });

Inside a backend function

Use the synchronous query bridge directly. Its results are already JavaScript values.

JavaScript
dehook.export(function addNote(data) {
  const text = String(data.text || '').trim().slice(0, 500);
  if (!text) throw new Error('A note needs some text');
  const id = query.put({ type: 'note', text: text });
  return { id: id, notes: query.search({ type: 'note' }) };
});

Filter, update, and paginate

JavaScript
dehook.export(function recentTasks() {
  query.update({ type: 'task', priority: { gt: 5 } }, { urgent: true });
  return query.search({ type: 'task', priority: { gt: 3 } }, { start: 0, limit: 10 });
});

Filters support equality values and eq, ne, gt, and lt. Search supports start and limit. Put adds an id and created_at timestamp.

Browser sessions

Dehook resolves a browser session automatically. Backend functions can read and update its session object. Local JavaScript calls use __dehook_local, matching the original local client behavior.

JavaScript
dehook.export(function visit() {
  session.visits = (session.visits || 0) + 1;
  return { visits: session.visits };
});

dehook.run('visit', {}, (response) => console.log(JSON.parse(response)));
JavaScript
dehook.export(function logout() {
  session.destroy();
  return { response: 'Session cleared' };
});

Session identity expires after 48 hours. The server's session registry is in memory and resets on a service restart. Sessions are a building block for your application, not a complete authentication system.

From browser to local JavaScript

Download the client for the active app. It includes your public and private tokens, so it connects to exactly the same backend as this page.

Terminal
Preparing your download
app.js
const dehook = require('./dehook.js');

// This is the function you exported from your browser.
dehook.run('hello', { name: 'JavaScript' }, (response, error) => {
  if (error) throw error;
  console.log(JSON.parse(response));
});

// You can call this from your browser immediately after it is exported.
dehook.export(function local() {
  return { response: 'Hello from JavaScript!' };
});

Local JavaScript works with Veloce and Node.js with built-in fetch. Choose one runtime.

Node.js
node app.js
Veloce
veloce --allow-read=. --allow-net=dehook.me app.js

export, run, query, exports, and import use the same syntax in both runtimes. The local client needs no Node.js or npm packages.

Native Veloce inside Dehook

Dehook embeds Veloce for every exported function. Your function gets native modules plus the host-provided query and session bridges.

What powers query? Dehook's query API uses a Rust bridge to its per-app JSON document store. It does not use Veloce ORM.

Native hashes and identifiers

Use Veloce's native crypto functions inside an exported function, then persist the result through Dehook's document bridge.

JavaScript
dehook.export(function fingerprint(data) {
  const text = String(data.text || '').slice(0, 8192);
  const fingerprint = {
    type: 'fingerprint',
    traceId: crypto.randomUUID(),
    sha256: crypto.sha256Hex(text)
  };
  const id = query.put(fingerprint);
  return { id: id, fingerprint: fingerprint };
});

dehook.run('fingerprint', { text: 'Hello Dehook' }, (response) => {
  console.log(JSON.parse(response));
});

The built-in crypto module is also available. For example, require('crypto').createHash('sha512').update(text).digest('hex') computes a native SHA-512 digest.

Parse and write YAML

YAML.load and YAML.dump call Veloce's native YAML parser and serializer.

JavaScript
dehook.export(function parseConfig(data) {
  const config = YAML.load(String(data.yaml || '').slice(0, 8192));
  return { config: config, yaml: YAML.dump(config) };
});

dehook.run('parseConfig', { yaml: 'name: Ada\nrole: builder' }, (response) => {
  console.log(JSON.parse(response));
});

Native numeric kernels

Veloce's kernel bridges numeric arrays to native code. This example computes a sum and dot product.

JavaScript
dehook.export(function calculate(data) {
  const left = data.left;
  const right = data.right;
  if (!Array.isArray(left) || !Array.isArray(right)
      || left.length !== right.length || left.length > 1000
      || !left.every(Number.isFinite) || !right.every(Number.isFinite)) {
    throw new Error('Pass two equally sized numeric arrays, up to 1000 items');
  }
  return {
    sum: Veloce.kernel.sumF64(left),
    dot: Veloce.kernel.dot(left, right)
  };
});

dehook.run('calculate', { left: [1, 2, 3], right: [4, 5, 6] }, (response) => {
  console.log(JSON.parse(response)); // { sum: 6, dot: 32 }
});

Can an exported function use Veloce ORM?

Direct ORM connections are currently blocked inside exported functions. The runtime's database bindings need app-owned connections and consistent database permissions before Dehook can expose them safely. Veloce's standalone ORM is separate from Dehook's query store.

Use query for persistent backend data today. For ORM-backed workflows, use it in your local Veloce program as shown below.

Use ORM in your local Veloce app

Your local Veloce program can use its own ORM database under the permissions you grant it. This example copies notes from your Dehook app into a separate local SQLite database.

sync-notes.js
const dehook = require('./dehook.js');
const orm = require('orm');
const path = require('path');

const notes = orm.table('notes', {
  id: orm.serial().primaryKey(),
  text: orm.text().notNull()
});
const db = orm.connect({ provider: 'sqlite', path: path.resolve('notes.sqlite') });
orm.migrate(db, { notes });

dehook.query({ search: { type: 'note' } }, (response, error) => {
  try {
    if (error) throw error;
    for (const note of JSON.parse(response)) {
      db.insert(notes).values({ text: String(note.text) }).execute();
    }
    console.log(db.select().from(notes).execute());
  } finally {
    db.close();
  }
});
Terminal
veloce --allow-read=. --allow-write=. --allow-net=dehook.me sync-notes.js

The local database is independent of the backend store. Running this example again appends another copy of the returned notes.

Other native APIs and permissions

APIInside an exported function
Crypto hashes and UUIDsAvailable; examples above
YAML parse and dumpAvailable
Numeric array kernelsAvailable
Dehook query and sessionAvailable through app-scoped bridges
Native ORM / SQL connectionsBlocked until app-scoped access is implemented
Filesystem, network, shell, and serversBlocked inside exported functions

Outside exported functions, your local Veloce program can use native APIs under its CLI permissions. That does not grant the remote function those permissions.

local-fetch.js
// Runs locally in Veloce, outside dehook.export(...).
const response = await fetch('https://dehook.me/dehook.min.js');
console.log(response.status);
Terminal
veloce --allow-net=dehook.me local-fetch.js
Exported functions must be synchronous. The current wrapper does not await returned promises. The 64 MiB heap limit and 500 ms interrupt apply to JavaScript execution; they do not impose equivalent limits on every blocking native call.

Call your function with curl

Publish a function

Use your private token to publish hello. This request is already configured for your active app.

Terminal · export
Preparing your publish request

Run the function

Export hello first, then copy this request. It already uses your active public token.

Terminal
Preparing your request

For another function, replace hello in the URL and send its input under data. The private token is not required to run public functions.

Claim a backend

Choose the app in Your workspace, then select Claim backend. Sign in with Google, GitHub or an email link and confirm the selected public app token. Your app ID, function URLs, documents and functions stay the same.

The previous anonymous private token stops working after the claim. It is removed from this browser only after the server confirms ownership. Manage the backend at Your account, or download a replacement deploy key there for local JavaScript and agents. Deploy keys grant app administration, so keep them private. Public function calls and public function source remain public.

Your own primary storage

Claiming with email, Google or GitHub keeps storage on the Dehook VPS until you explicitly change it. Claiming does not increase storage limits.

From your account, connect Google Drive or GitHub separately from sign-in. Drive creates a file in the consenting user's own account. GitHub accepts a private repository owned by the connected GitHub user. The 51lab developer account supplies the OAuth application, never shared user storage.

You can also connect a storage client on your own VPS from the account page. It uses outbound HTTPS and needs no public port. Download the configuration and client, start it, then verify and activate it as primary storage. VPS setup guide.

The selected provider stores the live app snapshot. The VPS keeps a working cache, reads the provider before each app operation, and confirms writes there before returning success. Provider outages and revision conflicts return errors; uncertain mutations must be inspected before retrying. Git history retains older snapshots. Existing Dehook limits still apply.

Going live

Use the public-only snippet in pages you share. The private token grants function publishing and direct document access.

Production HTML
Preparing your public snippet

Keep your configured local client private: its source contains your private token. Do not put secrets in exported functions; function source is readable using the public token. Implement authorization in exported functions before handling private data.

Changing apps

New backend in Settings creates a separate backend. Existing pairs stay in this browser's app selector. Download the client or save the pair before changing devices or clearing browser storage.

API reference

MethodWhat it doesNeeds private token
export(fn, callback?)Publish or overwrite a named functionYes
run(name, data, callback)Run a published functionNo
query(query, options?, callback?)Read or change app documentsYes
exports(callback)Get function names and source as an objectNo
import(name, callback)Get one function's sourceNo

Run and query callbacks receive (responseText, error). Exports and import callbacks receive (value, error). Export callbacks receive (responseText, error), and export immediately returns the public function URL.

Free apps have an 8 KiB document/session budget and 6 KiB of serialized function code, with bounded metadata bringing the total to 16 KiB. Functions are limited to 2 KiB of source and 8 KiB replies. Execution has a 500 ms JavaScript deadline and a 1-second outer worker deadline. Storage and request limits are enforced server-side. Private credentials can read usage through POST /app/usage and recover data through POST /app/export. Existing larger stores remain exportable and can shrink.

Backend settings

These settings apply to the app selected in Your workspace.

Credentials and client

App identifier
Keep this private
Download client

Backends in this browser

New backend creates a separate app. Previous apps stay in the app selector. Save your credentials before clearing browser storage.

Rotate this app's private token

Existing private keys will stop working. Download the client again after rotating.

Use an existing token pair