Matrix App Builder
SkillDatabases & dataBuild Matrix OS apps as Vite React TypeScript projects with matrix.json manifests, Matrix theme integration, Postgres-backed app data, and production build verification.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Matrix App Builder skill
What this skill tells your AI
The instructions your AI receives, as published by hamedmp/matrix-os in skills/matrix/app-builder/SKILL.md and read by ahel’s review.
When to Use
Use this when the user asks to build, create, fix, redesign, or publish a Matrix OS app.
Non-Negotiables
- Build user-facing apps in
~/apps/<slug>/. - Default to Vite, React 19, TypeScript, and
runtime: "vite". - CRM, roadmap, dashboard, admin, and data-heavy apps are still Vite React SPAs by default. Use Matrix/Postgres bridge APIs for data instead of creating Next.js API routes.
- Do not create Next.js,
.next/,app/router files,runtime: "node",serve.start, ornpm startunless the user explicitly requests a server runtime or Next.js. - Do not create plain HTML apps unless the user explicitly asks for a plain HTML app.
- Always create or update
matrix.json. - Always run
pnpm installwhen dependencies changed andpnpm buildbefore saying the app works. - Verify
dist/index.htmlexists. - Use injected Matrix theme variables and iframe-safe sizing. Custom apps should inherit the shell theme by default; add explicit app branding only when the user asks for it or the app has a clear domain reason.
- For UI, ALWAYS load and follow
matrix-design-systemandmatrix-app-ui-patterns. Key rules: inherit shell fonts through--matrix-font-sans/--matrix-font-mono, use Forest/Cream/Ember/Deep only as fallback palette values, gradient backgrounds (sand washes not flat), capsule buttons/inputs (50px radius), glass cards (22px radius), inline SVG or bundled local icons (never text characters or remote icon scripts), stable window-sized layouts, and stagger animations on mount. No exceptions. - Store structured app data through Matrix/Postgres bridge APIs, not ad hoc local databases.
- Never put provider secrets, API keys, or OAuth tokens inside the app directory.
- Do not use browser
localStorageas app persistence in the Matrix shell. Sandboxed iframes can throwSecurityError; usewindow.MatrixOS.dband keep local fallback paths test-only/no-op. - For default or first-party apps under
home/apps/**, keep manifests deterministic:runtime: "vite",build.output: "dist", schema columns declared instorage.tables, andiconpointing to a committed asset inhome/system/icons/.
Standard Structure
~/apps/<slug>/
matrix.json
package.json
vite.config.ts
tsconfig.json
index.html
src/
main.tsx
App.tsx
App.css
dist/
index.html
Manifest
Use this baseline and adjust the app name, description, category, icon, and storage tables:
{
"name": "My App",
"description": "A concise app description",
"slug": "my-app",
"version": "1.0.0",
"runtime": "vite",
"runtimeVersion": "^1.0.0",
"listingTrust": "first_party",
"icon": "my-app",
"category": "productivity",
"build": {
"command": "pnpm build",
"output": "dist"
},
"storage": {
"tables": {}
}
}
Valid storage column types include text, boolean, integer, float, timestamptz, jsonb, and uuid.
Naming
nameis the human, Title-Case label shown in the launcher, dock, and title bar — e.g."Calorie Tracker","Habit Garden". Make it short and real; never show the slug to users.slugis the lowercase, hyphenated id used in paths/URLs (^[a-z0-9][a-z0-9-]{0,63}$). Derive it from the name (calorie-tracker). It is internal — do not use it as a display string anywhere.
Icon (required — or the app shows a broken tile)
The launcher loads each app's icon from ~/system/icons/<icon>.svg (or .png) matching the manifest
icon field. If you don't ship that file, /icons/<icon> 404s and the app gets a broken/placeholder
icon. So always:
- Set
"icon": "<slug>"inmatrix.json(use the app slug unless you have a better concept name). - Create
~/system/icons/<slug>.pngusing the Matrix OS shipped-icon style: light premium iOS/macOS skeuomorphic app icon artwork, refined Apple-like product rendering, bright warm off-white or pale pastel background, subtle ceramic/glass depth, soft bevels, glossy highlights, realistic studio shadows, and a single large tactile 3D object or symbol that clearly represents the app. Keep the icon family aligned with Matrix OS forest, cream, ember, and deep accents. Do not include text, logos, watermarks, transparent backgrounds, black/dark dock backgrounds, empty padding, or a separate visible icon frame; the Matrix shell owns the final corner radius. Keep lighting and material treatment consistent with the shipped default app PNGs in~/system/icons/.
For first-party/default apps, prefer committed PNG icons from home/system/icons/. SVGs are acceptable
only for system chrome or simple compatibility fallbacks, not for newly generated app logos.
Data (Postgres via the MatrixOS bridge)
Apps run in a sandboxed, null-origin iframe (CSP connect-src 'self'), so a direct fetch() to
/api/bridge/* is blocked and localStorage throws SecurityError. Persist ONLY through the
injected bridge:
- Declare your tables in
matrix.jsonstorage.tables(above). The gateway provisions the Postgres schema automatically — at startup for shipped apps, and lazily on first query for apps you build now, so a freshly-built app'sdbcalls work without any restart. - In code use
window.MatrixOS.db(find/findOne/insert/update/delete/count/onChange). Guard forundefined(it's absent in unit tests), wrap every call intry/catch(log + user-visible error; never a bare catch), update local state optimistically, and reconcile ononChange. - For external/third-party APIs use
window.MatrixOS.proxyFetch(url)(allowlisted) — never a raw fetch. - Do NOT add a
localStoragefallback that runs in the shell; it throws in the sandbox. A guardedtry/catchlocalStorage path is acceptable only as a no-op for the unit-test environment.
Theme Inheritance
The shell injects --matrix-* tokens into every bridged app iframe and updates them on theme changes. Build app CSS on those tokens first, with literal colors only as fallbacks:
:root {
--app-bg: var(--matrix-bg, #FAFAF9);
--app-fg: var(--matrix-fg, #32352E);
--app-card: var(--matrix-card, #FCFCF8);
--app-primary: var(--matrix-primary, #434E3F);
--app-primary-fg: var(--matrix-primary-fg, #FAFAF5);
--app-accent: var(--matrix-accent, #D06F25);
--app-success: var(--matrix-success, #3A7D44);
--app-warning: var(--matrix-warning, #E0A12E);
--app-danger: var(--matrix-destructive, #D74A3A);
--app-border: var(--matrix-border, #D8D6C7);
}
Use the inherited shell fonts (var(--matrix-font-sans), var(--matrix-font-mono)) instead of loading remote font stylesheets. Do not use app-local blue, green, or purple palettes for default UI. If explicit app branding is needed, scope it to named brand tokens and keep system controls, focus states, status colors, and panels on the Matrix tokens.
Persistence Rules
- Declare every persisted field in
matrix.jsonstorage.tables, including audit fields such ascreated_at, best-score/best-time columns, and JSONB state payloads. - Load from bridge storage on startup; do not seed duplicate fallback rows after real rows load.
- Roll back optimistic UI changes on failed creates, updates, deletes, and reorders. Keep pending deletes filtered from visible state until the bridge confirms or rolls back.
- Serialize dependent writes that update ordering, best scores, stats counters, or history rows. Avoid racing two bridge writes that can overwrite each other's derived state.
- Keep browser-only helpers guarded for tests. In production shell iframes, direct
localStorageaccess and raw bridgefetch()calls are not reliable persistence.
First-Party Default Apps
When editing bundled default apps in this repo:
- Keep app manifests and schema in
home/apps/<slug>/matrix.json. - Reuse the shared default-app Vite build path; do not add stale per-app package/runtime fields unless the app truly needs them.
- Run
node scripts/build-default-apps.mjs home/appsbefore host-bundle work when default app source changed. - Prefer the shared
game-centericon for games unless a concrete shipped icon exists. - Verify app icon slugs against
home/system/icons/<slug>.svgor.png; never rely on runtime icon generation.
Scaffold Commands
Prefer copying Matrix's bundled Vite template when it exists:
cp -a ~/apps/_template-vite ~/apps/<slug>
cd ~/apps/<slug>
pnpm install --prefer-offline
pnpm build
test -f dist/index.html
If the template is not present, create the standard Vite files directly with react, react-dom, @vitejs/plugin-react, typescript, and vite.
Data Access
Use the injected window.MatrixOS.db bridge for structured data. Do not call /api/bridge/query
directly from app code; runtime apps load as sandboxed srcdoc iframes, and direct bridge fetches
are blocked by the shell's CORS/CSP boundary.
Example CRUD:
const db = window.MatrixOS?.db;
if (!db) throw new Error("Matrix data bridge is unavailable");
const tasks = await db.find("tasks", { orderBy: { created_at: "desc" } });
const created = await db.insert("tasks", { title: "Ship" });
await db.update("tasks", created.id, { done: true });
await db.delete("tasks", created.id);
Integrations
If the app needs Gmail, Calendar, GitHub, Slack, Drive, or another provider, use Matrix integration APIs or the matrix-integrations skill. The platform owns provider credentials. The app never stores provider secrets.
Verification
Before reporting done:
cd ~/apps/<slug>
pnpm build
test -f dist/index.html
node -e 'const m=require("./matrix.json"); if (m.runtime !== "vite" || m.build?.output !== "dist") process.exit(1)'
Then open the app in Matrix and check browser console for:
needs_build- 404s for app bundle or icon paths
- CORS errors from direct provider calls
- unhandled React errors
Signals
- GitHub stars
- 64
- Forks
- 20
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
matrix-app-builder- Source
- github.com/hamedmp/matrix-os