Drel/Docs/Build an App
Guide · four levels

Build a Drel App

A Drel app is not a proprietary format: standard browsers must always be able to load it. Start with the smallest level that solves your problem and add the next one only when you need it.

Develop with AI · Teach your agent the Drel app contract npx skills add dyyz1993/Drel --skill drel-app-development

Your app and the push service are independent

Your app is a web page. It deploys to any static host (Cloudflare Pages, Vercel, Netlify, Nginx) and needs no push backend of its own. A push service only delivers a wake-up — a URL or an appId + route — and is never required for the app to work.

Deploy together or apart. A gateway (serving signed app manifests) and a push relay are separate concerns. They can share one host and certificate, or run entirely apart. See Self-host Drel.

Level 1 — An ordinary web page

Zero barrier to entry. Open the URL in Drel, tap a push notification that opens it, or save it as a favorite tile. Two simple adaptations make it feel native:

<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">

body {
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
}

Support prefers-color-scheme for automatic dark mode, and read query parameters straight from window.location.search.

Level 2 — An app in the App Center

Add a standard Web App Manifest for a custom name, an icon and a standalone launch experience, then have an operator gateway sign a parent manifest.

{
  "schemaVersion": "1",
  "appId": "com.example.orders",
  "name": "Order Assistant",
  "startURL": "https://app.example.com/app/index.html",
  "allowedOrigins": ["https://app.example.com"],
  "capabilities": [],
  "routes": ["/app/index.html", "/app/orders/*"],
  "cache": { "strategy": "networkOnly", "version": "1", "persistent": false }
}

The routes array decides where a push can land. A push carrying appId and route opens that exact screen.

curl https://api.drel.app/push -H 'content-type: application/json' -d '{
  "deviceKey": "YOUR_KEY",
  "title": "New order",
  "appId": "com.example.orders",
  "route": "/app/orders/1024"
}'

How a manifest becomes trusted

A manifest is not trusted because it says so. It is trusted because it verifies against a public key the user confirmed.

Gateway signing and trust chain: you publish the manifest and the resource manifest; the gateway signs the canonical JSON with Ed25519; the phone verifies the signature and the public key id; the phone then pins the resource digest and each file digest.
One key, one job. The private key never leaves the gateway. The phone holds only the public key and the key id, and it drops the old grants when either changes. A remote update cannot swap the app identity, because the identity is bound to the confirmed origin.

Which fields are standard, and which are Drel's

Two different objects are involved, and mixing them up is the most common mistake.

ObjectStandard?Who signs itPurpose
manifest.webmanifest W3C standard Nobody — you publish it Name, icons, start URL, scope, display mode. Any browser and PWA host can read it.
Parent gateway manifest Drel The gateway, with Ed25519 App identity, allowed origins, routes, native action declarations, offline binding.
The parent manifest is not a Web App Manifest. It is a Drel-specific, signed document. The phone trusts it; it never trusts a self-signed page.

Standard Web App Manifest fields you can rely on

id
W3C
A stable identity for the web app. Drel binds it to the trusted origin — a remote update cannot silently replace a local app's identity.
name, short_name
W3C
Title and short title for the app card. When name is absent, Drel falls back to the page title or a user-chosen label.
icons
W3C
Icons for the app card. After an offline install only a verified in-package icon is used; network-only previews may fall back to the favicon. An app icon never replaces Drel’s own iOS icon.
start_url
W3C
The default screen after the user confirms. Must stay inside the confirmed origin and scope, and must not carry credentials or one-time tokens.
scope
W3C
Which navigations still count as this app. Navigation outside the scope opens as an ordinary page and does not inherit the app identity.
display
W3C
A presentation preference such as standalone. It is a hint, not a command — Drel keeps its back, exit, and security controls.
theme_color, background_color
W3C
Visual hints for the card, launch transition, and container theme.
shortcuts, screenshots, orientation
W3C
Accepted as compatibility data. Drel does not yet turn them into native actions, notification buttons, or permission entries.

Fields Drel adds

appId
Drel
The stable app identity used in the signed parent manifest. A page cannot change it by editing its own Web App Manifest.
allowedOrigins
Drel
The exact origins this app may use. Compared as scheme://host:port — never as a wildcard domain.
routes
Drel
The pages a push may open. A route outside this list is rejected.
capabilities_v2
Drel
Per-action native declarations. See the Bridge API reference for the exact shape.
cache
Drel
The offline strategy: networkOnly or manifest, plus the version, persistence, and resource manifest binding.
webManifest
Drel
The exact URL and SHA-256 of the standard manifest, so the phone can prove the file it fetched is the one that was signed.
signature
Drel
The Ed25519 signature and key id applied by the gateway. The phone verifies it before trusting anything.

One Drel-only extension inside the standard manifest

drel_offline
Drel
A prefixed block carrying the offline version, the resource manifest URL, and its SHA-256. The standard manifest cannot express "list every offline file and verify each one", so this block binds the resource set without polluting the standard fields.

Because the block is prefixed and handled separately, the same file still works as an ordinary PWA manifest in any other browser or host.

Level 3 — Strong offline package

A Web App Manifest alone does not make an app durable offline. Reliable offline launch requires a verified package and a user-initiated install.

Publish three files:

FileRole
package.jsonResource manifest: every file with its path, size, SHA-256 and MIME type.
manifest.webmanifestStandard manifest plus a drel_offline block pointing at the resource manifest and its digest.
Signed parent manifestThe POST /api/v1/html-apps body that the gateway signs with Ed25519.

Include every static asset — a missing chunk means a white screen offline. Keep API calls on the network, and label cached data honestly instead of faking it as live.

Hard limits: resource manifest ≤1MiB, ≤2000 files, ≤50MiB per file, ≤250MiB total.

Level 4 — Native actions

Declare each action in the signed manifest and call it behind a fallback. Read the Bridge API reference for the complete contract.

Ship checklist

  • Works in a plain browser without the Bridge.
  • Safe-area insets are respected and dark mode is readable.
  • start_url and scope carry no query string.
  • Every static asset is in the package with a valid hash.
  • Offline shows an honest fallback, not fake live data.
  • A push with appId + route lands on the intended screen.
  • No secret appears in start_url, a screenshot, or a log.

Live examples

Every level has a running fixture: strong offline, offline plus live data, declared actions and the reusable template.