mHub is an open platform that turns folders of simple files and ordinary websites into apps for phones, TVs and desktops. You do not need to be a programmer: if you can describe what you want, an AI can build it, and mHub gives it a place to run.
mHub is not one app. It is three parts that fit together, and every one of them is open. Two are foundations: a content format and a browser API. The third is the apps, and each of them stands on one foundation or both.
What you actually use. Dyscover turns addons into one browsable screen, and other apps each take one idea to every screen in the house. All of them are web apps: nothing to install. See the apps
An addon is a source of content: films, radio stations, podcasts, courses. Under the hood it is a folder of small text files on any web host, described by the Addon Protocol v2. An AI can write one for you. See the protocol
Open a normal website inside an mHub app and it can do things a browser never allows: read data from anywhere, survive a dead domain, live on your TV. One namespace, one script tag: the mHub Browser API. See the API
Dyscover stands on both foundations; the apps beside it show how far one foundation alone carries. All of them are live.
Give Dyscover addons and it turns them into one browsable screen: every catalog your addons expose, search across all of them, a library, a player that resumes where you left off. It is one web app that runs on phones, TVs and desktops alike, and handles touch, remote control and pointer itself.
It stands on both foundations: addons bring the content, and inside an mHub browser the Browser API adds the powers a plain page never has. You can even mount it on a page of your own, with your addons and your name on it: one HTML file.
Radio stations, podcasts and free audiobooks as one app. Waves is
one HTML file over the same Dyscover build: five audio addons, a teal
accent, and addonManager: "fixed", so the sources are set
and the user only switches them on and off. View source on it: the page
is the whole tutorial.
Kids TV. One addon, bright colours, German from the first screen
(locale: "de"), and addonManager: "hidden":
nothing to misconfigure from the couch. Also one HTML file over the
Dyscover build.
The addon is the data; what someone builds on top of it is the app. Mediathek is one HTML file straight on the ard-mediathek addon, no Dyscover underneath: rows, search, series with their episodes, subtitles, and a player that remembers where you stopped. The addon does the fetching, the page does the looks.
The other foundation on its own: Glance uses no addons at all. One HTML file on the Browser API, pulling live weather, headlines, market prices and the status of your own servers onto one calm screen. In a plain browser it degrades gracefully; inside an mHub app every tile wakes up.
Waves and Junior are exactly this: load the Dyscover build, give it an
element, call mount. This file, on any static host, is a
finished app with your addons, your name and your colours, and it follows
Dyscover releases by itself. No build step, no server code, no account.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=no">
<title>My App</title>
<link rel="stylesheet" href="https://dyscover.mhub.mx/latest/style.css">
<script type="importmap">
{ "imports": { "hls.js": "https://dyscover.mhub.mx/latest/hls.mjs" } }
</script>
</head>
<body>
<div id="app"></div>
<script type="module">
import { mount } from "https://dyscover.mhub.mx/latest/app.js";
mount({
addons: ["https://www.mhub.mx/dw"],
brand: { name: "My App", colors: { accent: "#00AAFF" } },
});
</script>
</body>
</html>
An addon is its manifest URL. The ones hosted here are listed on the addon index, and an AI can build you a new one.
Everything is optional. With no options at all you get Dyscover as it runs on its own domain: no addons installed yet, the whole addon system open, the Dyscover mark.
The sources the page ships with. A URL, or { url, enabled }
to ship one switched off.
addons: [ "https://www.mhub.mx/dw", "https://www.mhub.mx/kika", ] // ship one switched off: { url, enabled: false }
name, logo (sidebar), icon
(favicon) and four colour slots: accent,
primary, bg, text. The rest of
the palette is derived, so two colours restyle the whole app.
brand: { name: "Waves", logo: "/logo.svg", colors: { accent: "#2EC4B6" }, }
A string is the start language. An array is the languages on offer; a single entry hides the language row. Unset means the browser language.
locale: "de" // or the offer: locale: ["de", "en"]
How much of the addon system the user gets to see. Three modes:
"open" the user adds, removes and switches sources; your list seeds once, the user's decisions win from then on "fixed" your list and nothing else; the user only switches sources on and off "hidden" no addon screen at all; enabled is the state, on every boot
A line { id: "arte", enabled: true } in addons
switches one addon a source brought: the entry of a repository you want
running without spelling out its address.
Where to render: a selector or an element. The default is
#app, so most pages never set it.
el: "#app"
settings: false removes the settings screen and its menu
entry. startRoute picks the first screen.
settings: false, startRoute: "channels"
Tiles for the mHub browser home screen. Unset, the host gets one tile with your brand's name on it.
links: [ { id, name, icon, url }]
{ el, navigate, unmount }: the element it rendered into,
a way to switch screens from your own code, and a clean teardown.
The build is served from dyscover.mhub.mx with open CORS,
so the snippet above works from any origin as it stands.
/latest/ is a short redirect onto the current version
folder, so a page that loads it is up to date minutes after a release.
To freeze your app instead, put a version in the path: those folders
never change and cache for a year.
latest.json names
the current version.
/latest/app.js follows releases /v/0.1.0/app.js frozen forever
Hosting the files yourself works too. A version is four files: download them, put them next to your page, change the three URLs in the snippet to relative ones. Any static host works; when it updates is then yours to decide.
/v/0.1.0/app.js the app, one module /v/0.1.0/style.css the look /v/0.1.0/hls.mjs the stream engine /v/0.1.0/index.html mount() with no options
Any modern browser. The addons hosted on mhub.mx answer with CORS headers, so a plain browser can browse them; what a stream allows is up to the addon's source. Inside an mHub browser the same page gets the full Browser API: cross-origin fetching, streams that need their own headers, storage that follows the site.
An addon is a source of content: films, radio stations, podcasts, recipes, courses. Put the folder anywhere on the web, add its address in an app, and everything in it appears there, browsable. Version 2 of the protocol needs no server: every endpoint can be a static file on any CDN. Every existing v1 addon keeps working; the apps translate between the two, and v1 stays supported.
All endpoints are GET, all cacheable, all hostable as plain files. One optional POST for the task system.
/mhub-addon.json /catalog/{type}/{catalogId}.json /item/{type}/{id}.json /source/{type}/{id}.json
Catalogs, item details, sources, resolvers and subtitles are separate resources. One addon can provide all of them or just one.
"resources": ["catalog", "item", "source", "resolve", "subtitle"]
An endpoint can answer with a task instead of data. The app fetches with the user's own IP, so a static addon works with geo-blocked APIs.
// addon stays static, // the client does the trip
A minimal addon is one manifest and one catalog file. Upload both to any static host and it works. The spec is OpenAPI 3.1 with generated TypeScript types and a zero-dependency client library, MIT licensed, on GitHub.
{ "id": "my-movies", "name": "My Movies", "specVersion": 2, "resources": ["catalog"], "types": ["video"], "catalogs": [{ "id": "all", "name": "All", "type": "video" }] }
The whole addon format fits into a single prompt, and you never have to read it. Copy the prompt, add your idea, and the AI answers with a folder of files. Upload them anywhere and your addon is live.
It points the AI at the spec. Works with any assistant that can read links, like ChatGPT or Claude.
"My favorite radio stations." "Every film by one director." "The public lectures of my university." Anything with a list.
Any static host works, GitHub Pages is free. Add the address in an mHub app and browse your own addon.
Read the mHub Addon Protocol v2 spec at https://raw.githubusercontent.com/mediahubmx/protocol/main/openapi.yaml and build an addon for me. An addon is a folder of static JSON files, ready to upload to any web host. Answer with every file: its full path, then its complete content. My addon: [describe your idea here]
If your assistant cannot open links, paste the spec file into the chat instead. The spec and complete example addons live on GitHub.
Everything lives under one namespace: window.mhub,
and every host serves the full core. Feature-detect the namespace once,
then just call it. No SDK, no build step, no account: one script tag is
the whole setup, and self-hosting it is fine.
The API is built and documented, and it arrives with the next release of
the apps. Write against it today and your page keeps working either way:
where no host is present, window.mhub
is simply absent and your site stays an ordinary website.
Fetch anything. No CORS, and request headers the browser normally reserves for itself.
// cross-origin, readable const r = await mhub.fetch(url);
Some media needs its own request headers to play. One call, and the host picks the right route on every platform.
const s = await mhub.openStream( { url, headers }); video.src = s.url;
Key/value storage that follows your site, not the domain it happens to run on. No prompt, no setup.
await mhub.storage.set(k, v);
One domain down, your site still loads. Serve one small file on every domain; the browser finds it, verifies your mirrors and fails over by itself. No code.
{ "id": "mysite", "endpoints": [ "https://a.example", "https://b.example" ] }
A tile on the browser home screen. The way back to your site, surviving between visits.
mhub.setLinks([ { id, name, icon, url }]);
The address bar becomes your site's search. Queries and suggestions go straight to your page.
mhub.setSearch({ placeholder, onQuery, onSuggest });
Know where you run and what the host can do, from phone to TV, before first paint.
if (mhub.device.isTV) tvUi();
A page that runs as an app tells the host what it handles itself: its
own back navigation (setBackHandler), edge-to-edge layout
(setEdgeToEdge), a full-screen player
(setImmersive), and the door out (exit); a
meta tag in the HTML says it is an app built for the remote.
Every member, with the permission model and the mirror system, is in
the API reference.
mhub.setBackHandler(depth); mhub.setImmersive(true); <meta name="mhub" content="app dpad">
The obvious use is a media library on your TV. But the powers underneath are general, and most of these are impossible as a normal website. A few things a single web page can be inside mHub:
Pull any feed straight from its source. Normal browsers block that; inside mHub it just works. Your reading list follows you, and it reads well from the couch.
Weather, headlines and the price of things you care about, as one calm screen on the TV. We built this one; it is the Glance screenshot further up, and it runs live.
Talk to the devices in your own network: lights, NAS, printer, router. Browsers refuse local requests like that; the mHub app makes them for you.
Fetch feeds, play episodes, resume where you stopped. One page, no server, no app store.
Check shops for prices and availability, keep a watchlist. The page asks; you see the answer on any screen.
A friendly face for open archives like Archive.org or Project Gutenberg. Browse, search and read them like an app.
The API reference covers every member, the permission model, and the mirror system. The protocol spec ships with complete example addons you can copy.