Architecture Overview
AI Supreme Council is a zero-hosting bot management platform that runs entirely in the browser. The production output is a single index.html file (~980 KB) assembled from 80 modular source files. There are no external runtime dependencies -- only JavaScript and WASM ship to the browser.
Single-File Architecture
The app is a single self-contained HTML file. During development, it is split into src/ parts for smaller context windows and focused editing. The build.sh script concatenates all 80 parts back into index.html in a strict order.
src/shell-head.html \
src/shell-style.html |
src/shell-body.html |
src/core-boot.js |
src/core-auth-main.js | build.sh
... | ---------> index.html (~980 KB)
src/settings-main.js |
src/miniprograms.js |
src/pwa.js |
src/shell-bottom.js /
Never edit index.html directly. Always edit the corresponding file in src/, then run ./build.sh to reassemble.
Global Namespace
All modules register on the window.AIS global namespace. The namespace is initialized in src/core-boot.js:
window.AIS = { version: '2.0.0', type: 'aiscouncil' };
AIS.PLATFORM_VERSION = '1.0.0';
AIS.ABI_VERSION = 1;
Cross-module communication uses a lightweight event bus:
AIS.on('event', handler); // subscribe
AIS.off('event', handler); // unsubscribe
AIS.emit('event', data); // publish
Module System: AIS.lazy()
Modules use a Qwik-style lazy hydration pattern. The factory function is deferred until the module is first accessed via a property getter on AIS:
if (!AIS.Council) AIS.lazy('Council', function() {
'use strict';
// module code here
return { run, renderCouncilMessage, estimateCost };
});
On first access (e.g., AIS.Council.run()), the getter fires, executes the factory, replaces itself with the returned module object, and returns it. Subsequent accesses hit the plain value with no overhead.
WASM kernel modules can still override a JS module by setting AIS.Council = wasmModule before the first access, because the property is defined as configurable: true.
Module Categories
Core Modules (always loaded)
Core modules are defined inside a single <script> block (core-boot.js through core-end.js). They execute eagerly at page load and form the foundation of the app.
| Module | File | Purpose |
|---|---|---|
AIS.Auth | core-auth-main.js, core-auth-local.js, core-auth-init.js | WebAuthn/Passkey + OAuth login, session management, auth cookie |
AIS.Billing | core-billing.js | Subscription tier, trial, managed plan |
AIS.Codec | core-codec.js | Base80 encoding, VLQ versioning, deflate compression |
AIS.Storage | core-storage.js | IndexedDB + optional SQLite WASM, offline-first |
AIS.Providers | core-providers.js, core-providers-builtin.js | LLM provider registry, SSE streaming factory |
AIS.UI | core-ui.js | DOM utilities, markdown renderer, toast notifications |
AIS.Session | core-session.js | Bot session CRUD (IndexedDB-backed) |
AIS.Chat | core-chat.js | Chat history, streaming message send/receive |
AIS.Config | core-config.js | Bot config panel bindings, URL sync |
AIS.App | core-app-botlist.js, core-app-switch.js, core-app-events.js, core-app-search.js, core-app-init.js | Application controller, init, routing, bot management |
WASM-Replaceable Modules (lazy-loaded)
Each WASM-replaceable module lives in its own <script> block and uses the AIS.lazy() pattern. They only execute when first accessed.
| Module | File | Purpose |
|---|---|---|
AIS.Registry | registry.js | Community model registry, 24h cache, GitHub fallback |
AIS.Grid | grid.js | Model table/card renderer (compiled from TypeScript) |
AIS.Council | council.js | Multi-model deliberation engine (7 styles) |
AIS.Wizard | wizard.js | Dual-mode first-run setup wizard |
AIS.Vision | vision.js | Image input for vision models (paste/upload) |
AIS.Memory | memory.js | Persistent per-bot key-value memory |
AIS.ImageGen | imagegen.js | Image generation (DALL-E, Grok Imagine, OpenRouter) |
AIS.Tools | tools.js | Tool/function calling format normalization |
AIS.Reminders | reminders.js | Scheduled messages via /remind command |
AIS.Themes | themes.js | Visual theme system |
AIS.Templates | templates-registry.js | System prompt templates, welcome screens |
AIS.ModelPicker | model-picker.js | Sortable model browser |
Infrastructure Modules (lazy-loaded)
| Module | File | Purpose |
|---|---|---|
AIS.ModuleLoader | moduleloader.js | Hot-swap module lifecycle, OPFS cache |
AIS.Plugins | plugins.js | Plugin system: manifest validation, hooks |
AIS.MCP | mcp.js | Model Context Protocol (tools + resources) |
AIS.Channels | channels-core.js, channels-whatsapp.js, channels-adapters.js, channels-stubs.js | Channel adapters (Telegram, Discord, Matrix, Slack, WhatsApp) |
AIS.Sandbox | sandbox.js | WASM tool sandbox (Pyodide, QuickJS, SQLite) |
AIS.Publish | publish.js | SEO + static HTML publishing |
AIS.Perf | perf.js | Performance monitoring |
AIS.P2P | p2p.js | P2P collaboration via WebRTC + CRDTs |
Platform Modules
| Module | File | Purpose |
|---|---|---|
AIS.Settings | settings-main.js | Global settings dialog (all sections) |
AIS.I18n | i18n.js | Internationalization |
AIS.MiniPrograms | miniprograms.js | Mini-program runtime (sandboxed iframes) |
AIS.Docs | docs.js | Inline docs viewer |
AIS.Profiles | profiles.js | Multi-model profile/council templates |
AIS.Cron | cron.js | Browser-based scheduler |
CSS Architecture
The app uses a classless CSS approach. Styles target semantic HTML elements and IDs instead of classes.
| Selector Type | Example | Use Case |
|---|---|---|
| ID | #header, #sidebar-left, #config-body | Unique layout containers |
| Semantic element | #messages > article, article menu button | Messages, actions, navigation |
| Data attribute | [data-from="user"], [data-variant="primary"] | Variants (message role, button type) |
| State class | .active, .collapsed, .mobile-open | JS-toggled states |
| Component class | .council-member-row, .status-dot | Dynamic JS-created components |
| Utility class | .f1, .sc, .dim | One-off inline style replacement |
Semantic HTML elements used: <header>, <main>, <aside>, <section>, <footer>, <nav>, <article>, <menu>, <details>, <dialog>, <output>.
Classes are only used for three cases: state toggles (.active, .collapsed), dynamic JS components (.council-member-row), and utility shorthands (.f1, .sc). If you can target an element with a semantic selector or data attribute, do that instead of adding a class.
Storage Architecture
Storage is split between synchronous and asynchronous stores:
| Storage | Keys | Reason |
|---|---|---|
| localStorage (sync) | ais-theme, ais-apikey-*, ais-user, ais-ollama-endpoint | Sync reads needed at boot time |
| IndexedDB (async, unlimited) | ais-bots, ais-chat-*, ais-addon-manifests | Large data, no 5 MB limit |
| SQLite WASM (optional, OPFS) | Binary blobs | Lazy-loaded for concurrent access |
On first boot, AIS.Storage.init() auto-migrates data from localStorage to IndexedDB.
Provider Architecture
Providers are registered via AIS.Providers.register(). Most providers use the shared openaiCompatible() SSE streaming factory, which handles:
- Server-Sent Events parsing
- Token-by-token streaming callbacks
- AbortController signal support
- Token counting from response headers
Six built-in providers are registered in core-providers-builtin.js:
| Provider | API Style | Auth | Notes |
|---|---|---|---|
| Anthropic | Native (Messages API) | x-api-key header | Custom streaming format |
| OpenAI | OpenAI-compatible | Bearer token | Standard SSE |
| xAI | OpenAI-compatible | Bearer token | Grok models |
| Google Gemini | Native (Gemini API) | ?key= query param | Avoids CORS preflight |
| OpenRouter | OpenAI-compatible | Bearer token | 300+ models, free tier |
| Ollama | OpenAI-compatible | None | Local LLMs, auto-detects models |
All API keys are stored locally (localStorage['ais-apikey-{provider}']). Keys are sent directly from the browser to the provider -- never proxied through any server.
Design Principles
The codebase follows strict design principles that every change must satisfy:
- HTML5 native first -- Use
<dialog>,<output>,hiddenattribute, CSS animations before JS equivalents. - Zero polling -- No
requestAnimationFrameloops, nosetInterval, nosetTimeoutfor animation. Event-driven only. - Passive listeners -- All non-cancelable events use
{ passive: true }. - Event delegation -- Single listener on container, not per-item listeners on dynamic lists.
- 14px minimum font -- All text must be readable by vision LLMs. No text below 14px.
- VLM-friendly layout -- 48px+ click targets, large toggles, high contrast. Settings and menus display all primary items without scrolling on 1920x1080.
- Reduced motion --
@media (prefers-reduced-motion: reduce)disables all animations. - CSS containment --
contain: stricton sidebars,content-visibility: autoon scrollable lists.
Before adding any feature, ask the four decision-gate questions:
- Can the browser do this natively?
- Does this require a server? (If yes, make it optional.)
- Does this scale to 1 million bots per device?
- What are the memory and CPU costs?
WASM Kernel
The optional WASM kernel (kernel/) is written in Zig and compiles to ~5.5 KB. It provides low-level primitives for the module system: slot management, hook dispatch, ring buffer I/O, and write-ahead logging.
The kernel operates on a 64 MB SharedArrayBuffer with defined memory regions for module segments, ring buffers, WAL, and scratch arena. JS modules can be replaced by WASM equivalents at runtime without breaking the lazy-loading contract.
API Architecture
The platform uses split Workers for different concerns:
| Worker | Domain | Purpose |
|---|---|---|
aiscouncil-api | api.aiscouncil.com | Billing, usage, key vending, geo |
aiscouncil-auth | auth.aiscouncil.com | OAuth callbacks, token verification |
API base URLs are set at boot from domain detection and can be overridden via localStorage['ais-api-base'] for local development.