radex.maPlay

Build mini apps for radex.ma

Mini apps are small web apps (HTML, CSS, JavaScript) that open instantly inside the radex.ma app, with no install. You build them here, test them in a phone simulator, pass a security review, and publish a signed package that every Radex user can open.

Quick start

  1. Describe itOpen the studio and tell the AI designer what you want, in Darija, French or English. Or start from the example (a spa booking app) and edit the code.
  2. Try itThe simulator on the right runs your app with the real Radex SDK. Switch light/dark, tap through every screen, watch the console.
  3. PublishPress Publish. The security audit and an AI review run; if they pass, radex.ma signs your package and it appears in the Apps tab.
Your work is saved in this browser. Your developer key is created in this browser the first time you publish, and it owns your app's id: keep using the same browser to publish updates.

The AI designer

Write what you want the way you'd explain it to a person: who it's for, the screens, the data. Then keep chatting to change it ("make the buttons bigger", "add a favorites tab", "zid search b smiya").

Project files

FileWhat it is
manifest.jsonName, icon, version and exactly what the app may use. Required.
index.htmlThe first page (or whatever entry says). Required.
*.js, *.css, other *.htmlYour code. Keep it readable: no minified or obfuscated code.
data/<name>.jsonA JSON array your app reads with radex.query("<name>"). Declare it in collections.
Images, fontssvg png jpg webp gif woff2. Everything must be inside the package.

Every page includes the SDK, which the phone provides (never ship your own copy):

<link rel="stylesheet" href="/sdk/radex-ui.css">
<script src="/sdk/radex.js"></script>

manifest.json

{
  "id": "hammam_spa",                 // a-z 0-9 _, 3-32 chars, can't contain "radex"
  "name": "حمام و مساج",              // up to 30 characters
  "version": "1.0.0",                 // bump it for every update
  "icon": "spa",                      // one of the icon names below
  "color": "0xFFB5651D",              // tile color in the Apps tab
  "description": "One line people see in the Apps tab",
  "entry": "index.html",
  "permissions": ["payment", "storage"],   // only what you use: location, payment, storage
  "collections": ["centers"],              // data/centers.json
  "allowedDomains": []                     // network hosts; empty = works offline
}

Icons: taxi, food, channels, phone, car, moto, clothes, home, laptop, pets, bag, cash, storage, bolt, key, pin, star, check, search, error, heart, time, cart, person, flag, plane, train, city, cafe, chat, shield, apps, spa, event

Network: any host in allowedDomains is shown to people before they open your app, and the phone blocks every other host. Use HTTPS hosts only, at most 5.

SDK reference

Every call returns a Promise and is checked by the phone against your manifest. A refused call rejects with permission_denied:<what>.

CallReturnsNeeds
radex.query(name, params)The array in data/name.jsoncollections
radex.storage.get(key) / .set(key, value)Your app's own storage on the phone (strings, 64 KB each)storage
radex.location(){lat, lng, label}location
radex.pay(amountMad, description){ok, method}, or rejects if the person cancelspayment
radex.toast(text)Shows a short message for 1.5 s—
radex.appInfo(){id, name}—
radex.icon(name, size)Inline SVG string (current text color)—
radex.esc(value)HTML-escaped text. Use it for every value you put in innerHTML.—
radex.ui.skeleton(el, n)Loading placeholders—
radex.ui.error(el, message, retry)Error state with a retry button—
radex.ui.sheet(html, onClose)Bottom sheet, returns {el, close}—
const E = radex.esc;
radex.ui.skeleton(list, 4);
const items = await radex.query("items");
list.innerHTML = items.map((x) => `<div class="cell">${E(x.name)}</div>`).join("");

try {
  await radex.pay(total, "Order · 3 items");   // radex.ma shows its own confirmation sheet
  radex.toast("Order confirmed");
} catch { radex.toast("Payment cancelled"); }
Payments are confirmed by radex.ma, not by your app. The phone shows the amount in its own sheet; your app can't draw over it or press it. Cash on delivery for now.

UI kit

radex-ui.css matches the radex.ma app, in light and dark, right-to-left first. Set your color with :root { --accent: #B5651D; --accent-soft: rgba(181,101,29,.12) }.

GroupClasses
Layoutpage page--bar row row--between col grow gap-8 mt-8
Textt-22 t-17 t-15 t-14 t-12 bold muted faint accent ellipsis section-title price
Surfacescard card--pad card--tap cells cell cell--tap cell__icon cell__body cell__title cell__desc
Controlsbtn btn--primary btn--secondary btn--ghost btn--danger btn--block btn--sm chips chip options option stepper search
Feedbackbadge notice state state__icon state__title skeleton spinner
Otheravatar cover stars steps step bottom-bar divider fade-in
Theme tokens--bg --surface --surface-2 --ink --ink-2 --ink-3 --line --accent --danger

Design rules

The simulator

It runs the phone's exact SDK files and the same rules as the phone: undeclared permissions are refused, network is limited to your domains, payments open the radex.ma sheet. The console shows every SDK call and everything that was blocked; fix those before publishing.

Security rules

Every publish runs an automatic audit, then an AI review of what the app does. Any blocking finding stops publishing, with the file and line. The phone enforces the same rules again at runtime.

BlockedWhy
eval, new Function, string timers, obfuscated codeCode must be the code that was reviewed.
Remote scripts, import() from the webEverything that runs must be inside the signed package.
Iframes, window.open, WebSocket, WebRTC, sendBeacon, workers, cookiesThey can move data around the network rules.
Password, card, bank or verification-code fieldsPeople sign in with radex.ma and pay through radex.pay().
URLs not in allowedDomainsUndeclared network access.
Names or ids with "radex" or "official", reserved idsImpersonation.
Scams, data harvesting, misleading payments (AI review)Fake prizes, asking for ID cards, hidden charges.
If the AI review is unavailable, nothing is published until it can run. Try again later.

Publishing and updates

Command line

Prefer your own editor? Keep your files in a folder and publish with Node 20+:

# one-time: your developer key (keep it safe; it owns your app ids)
node publish.mjs https://play.radex.ma ~/.radex-dev-key.pem ./my-app

The script (in the radex.ma repository, server/play/scripts/publish.mjs) signs radex-publish\n<id>\n<version>\n<sha256 of your files> with your Ed25519 key and calls POST /api/publish.

Package format (.rdx)

"RDX1" | version 1 | flags 0 | header length (2 bytes)
header JSON  {id, version, name, size, files, sha256, dev, devSig, kid, ts, audit}
signature    Ed25519 by the radex.ma platform key, over "RDX1" ‖ header
payload      deflate-raw of { u16 path length, path, u32 size, bytes } for each file

API: GET /api/catalog, GET /api/apps/<id>/<version>.rdx, GET /api/platform (public keys).

What happens on the phone

FAQ

Is it free?

Yes, during the beta.

Can my app call my own server?

Yes: add its hostname to allowedDomains. People will see it, and the phone blocks every other host.

Can I use React or Vue?

Yes, if the built files are in the package and readable. Small vanilla JavaScript apps open fastest.

I lost my developer key.

The key owns your app id, so updates need it. Publish under a new id, or contact radex.ma to recover the old one.