Zum Hauptinhalt springen

Sideloading Apps

Sideloading lets you install mini-programs outside the official App Store registry. This is the primary workflow during development and also how users install apps shared directly by developers.

There are three sideloading methods:

MethodInputBest For
URL InstallManifest URLHosted apps, development with a local server
HTML UploadSingle .html fileQuick prototyping, single-file apps
.ais BundleZIP file (.ais extension)Multi-file apps, offline distribution

All three methods are available in the Sideload section of the Apps panel.


URL Install

The standard way to install a mini-program from a hosted source.

How It Works

  1. You provide a URL pointing to a manifest.json file
  2. The platform fetches and validates the manifest
  3. The entry HTML file is fetched from {base_url}{entry}
  4. A permission dialog shows the app name, description, and requested permissions
  5. On approval, the manifest and cached HTML are stored in IndexedDB
  6. The app appears in the installed apps grid

Steps

  1. Open the Apps panel (click the apps icon in the left sidebar)
  2. Scroll to the Sideload section
  3. Paste the manifest URL into the text field:
    https://example.com/my-app/manifest.json
  4. Click Install
  5. Review permissions and click Allow

Development with Localhost

During development, serve your app locally and install from localhost:

cd my-app
python3 -m http.server 8080

Then install from: http://localhost:8080/manifest.json

tipp

Most local HTTP servers serve files with permissive CORS headers by default. If you get "Failed to fetch manifest" errors, make sure your server includes Access-Control-Allow-Origin: * in its responses.

Updating a Sideloaded App

The platform caches entry HTML at install time. To pick up code changes:

  1. Uninstall the app (click the X button on the app card)
  2. Reinstall from the same URL

There is no automatic update mechanism for sideloaded apps. Registry apps check for updates using version comparison during the registry refresh cycle.


HTML Upload

The quickest way to test a mini-program. Upload a single HTML file and the platform creates a synthetic manifest automatically.

How It Works

  1. You select a .html or .htm file from your computer
  2. The platform reads the file content (max 5 MB)
  3. A synthetic manifest is generated:
    • name: derived from the filename (lowercase, hyphens, max 64 chars)
    • version: 1.0.0
    • entry: index.html
    • base_url: local://
    • permissions: ["storage"] (minimal)
    • description: "Sideloaded from {filename}"
  4. A permission dialog is shown
  5. The HTML is cached as the entry content

Steps

  1. Open the Apps panel
  2. In the Sideload section, click Upload App
  3. Select your .html file
  4. Review permissions and click Allow

Limitations

  • Single file only -- External CSS, JS, and images referenced by relative URLs will not load because the iframe has a null origin and base_url is set to local://
  • Minimal permissions -- The synthetic manifest only grants storage. If your app needs chat:read or other permissions, use URL Install or a .ais bundle instead
  • 5 MB max -- Files larger than 5 MB are rejected
  • No manifest customization -- You cannot set a custom description, icon, or permissions
info

HTML Upload is designed for rapid prototyping. For apps that need multiple files or specific permissions, use a .ais bundle or URL Install.

Making Single-File Apps Work

To make the most of HTML Upload, keep everything inline in your HTML:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
/* Inline all CSS here */
body { font-family: system-ui; padding: 16px; color: #e0e0e0; background: #1a1a2e; }
</style>
</head>
<body>
<h1>My App</h1>
<script>
// Inline all JavaScript here
ais.ready(function() {
ais.ui.toast('App loaded!');
});
</script>
</body>
</html>

Images can be embedded as data URIs:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." />

.ais Bundle

A .ais bundle is a standard ZIP archive with a .ais file extension. It contains the manifest, entry HTML, and all assets. The platform extracts the ZIP and inlines all referenced assets into a single HTML document at install time.

How It Works

  1. You upload a .ais file (max 50 MB)
  2. The platform parses the ZIP using native DecompressionStream
  3. manifest.json is extracted and validated from the ZIP root
  4. The entry HTML file (referenced by entry in the manifest) is extracted
  5. All other files in the ZIP are available as assets for inlining
  6. Asset inlining transforms the HTML:
    • <link rel="stylesheet" href="style.css"> becomes <style>{CSS content}</style>
    • <script src="app.js"></script> becomes <script>{JS content}</script>
    • <img src="icon.png"> becomes <img src="data:image/png;base64,{base64}">
    • url(bg.png) in inline styles becomes url(data:image/png;base64,...)
  7. The permission dialog shows the manifest's permissions
  8. The inlined HTML is cached in IndexedDB

Bundle Structure

my-app.ais (ZIP archive)
|-- manifest.json (required, at root)
|-- index.html (required, referenced by manifest "entry")
|-- style.css (optional, inlined into <style>)
|-- app.js (optional, inlined into <script>)
|-- icon.png (optional, inlined as data URI)
|-- images/
| |-- logo.svg (optional, inlined as data URI)
|-- fonts/
|-- custom.woff2 (optional, inlined as data URI)

Creating a Bundle

# Navigate to your app directory
cd my-app

# Verify your files
ls
# manifest.json index.html style.css app.js icon.png

# Create the bundle
zip -r ../my-app.ais manifest.json index.html style.css app.js icon.png

Or zip the entire directory:

cd my-app
zip -r ../my-app.ais .
vorsicht

The manifest.json must be at the root of the ZIP, not inside a subdirectory. If your ZIP has a structure like my-app/manifest.json, the platform will not find it.

What Gets Inlined

The asset inliner processes the entry HTML and replaces relative references with inline content:

PatternReplacement
<link rel="stylesheet" href="X"><style>{content of X}</style>
<script src="X"></script><script>{content of X}</script>
<img src="X"><img src="data:{mime};base64,{data}">
<audio src="X"><audio src="data:{mime};base64,{data}">
<video src="X"><video src="data:{mime};base64,{data}">
<source src="X"><source src="data:{mime};base64,{data}">
url(X) in inline stylesurl(data:{mime};base64,{data})

References that start with data:, http://, https://, or // are left unchanged -- only relative paths are inlined.

Supported MIME Types

The platform detects MIME types from file extensions:

ExtensionMIME Type
.csstext/css
.js, .mjstext/javascript
.jsonapplication/json
.pngimage/png
.jpg, .jpegimage/jpeg
.gifimage/gif
.svgimage/svg+xml
.webpimage/webp
.icoimage/x-icon
.wofffont/woff
.woff2font/woff2
.ttffont/ttf
.mp3audio/mpeg
.mp4video/mp4
.webmvideo/webm
.oggaudio/ogg
.wavaudio/wav

Example: Multi-File App Bundle

manifest.json:

{
"name": "dashboard",
"version": "1.0.0",
"abi": 1,
"type": "mini-program",
"title": "Dashboard",
"description": "Chat analytics dashboard",
"entry": "index.html",
"base_url": "local://",
"permissions": ["storage", "chat:read", "auth:read", "ui:toast"]
}

index.html:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Dashboard</h1>
<img src="logo.svg" alt="Logo" width="64">
<div id="stats"></div>
<script src="app.js"></script>
</body>
</html>

style.css:

body { font-family: system-ui; padding: 16px; color: #e0e0e0; background: #1a1a2e; }
h1 { font-size: 22px; }
#stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }

app.js:

ais.ready(async function() {
var history = await ais.chat.getHistory(100);
document.getElementById('stats').textContent = history.length + ' messages';
});

After bundle installation, the platform produces a single inlined HTML:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>body { font-family: system-ui; padding: 16px; color: #e0e0e0; background: #1a1a2e; }
h1 { font-size: 22px; }
#stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }</style>
</head>
<body>
<h1>Dashboard</h1>
<img src="data:image/svg+xml;base64,..." alt="Logo" width="64">
<div id="stats"></div>
<script>ais.ready(async function() {
var history = await ais.chat.getHistory(100);
document.getElementById('stats').textContent = history.length + ' messages';
});</script>
</body>
</html>

File Size Limits

MethodMax Size
HTML Upload5 MB
.ais Bundle50 MB
URL InstallNo hard limit (constrained by browser memory)
warnung

Large bundles with many embedded images or media files can result in a very large cached HTML string in IndexedDB. Keep bundles lean -- compress images before including them, and consider using external URLs for large media assets.


Security

Sideloaded apps get the exact same sandbox as registry apps:

  • sandbox="allow-scripts allow-forms" (null origin)
  • No access to parent DOM, localStorage, or cookies
  • Communication exclusively through the window.ais postMessage bridge
  • Permissions enforced by the host platform (not the iframe)

The only difference from registry apps is the absence of a trust badge (Community, AI Verified, or Verified) in the apps grid. Sideloaded apps have no badge.

info

Sideloading is the "Direct Install" tier of the marketplace distribution model. It is the web's native distribution mechanism -- install any app from any URL, with the sandbox providing baseline security.


The Sideload UI

The Sideload section in the Apps panel provides:

  1. URL input field -- Paste a manifest URL and click Install
  2. "or" divider
  3. Upload App button -- Opens a file picker accepting .html, .htm, .ais, and .zip files
  4. Help text -- "Accepts .html single-file apps or .ais bundles (ZIP with manifest.json)"

All three methods trigger the standard permission dialog before completing installation. Installed apps appear in the Installed section above, with Open and Uninstall buttons.