Hosting
Muhkoo can host your app’s frontend too. Every app gets a DNS subdomain —
https://<slug>.apps.muhkoo.dev (the <slug> is your app’s slug) — and you
deploy your built static site there. No separate hosting account, no DNS to wire
up. Deploys are content-addressed (only changed files upload), instant (an atomic
release flip), and rollback-able.
Deploy auth — the app secret key
Section titled “Deploy auth — the app secret key”Deploys are authorized by your app’s secret key (mk_live_sk_… /
mk_test_sk_…) as a bearer token (a developer session also works). The secret key
is server-side only — never ship it in the browser bundle (that’s the
publishable pk). In CI it’s a repository secret. Rotate or revoke it in the
portal like any key.
Deploy
Section titled “Deploy”The simplest path is the deploy script shipped by the
app-builder template (scripts/deploy.mjs). It
walks dist/, uploads only changed files, commits a release, and prints the URL:
npm run buildMUHKOO_DEPLOY_KEY=mk_live_sk_… MUHKOO_APP_ID=<appId> node scripts/deploy.mjs# or just: npm run deployGitHub CI/CD
Section titled “GitHub CI/CD”Deploy on every push to main with the bundled .github/workflows/deploy.yml. Set
these repository secrets (Settings → Secrets and variables → Actions):
| Secret | What |
|---|---|
MUHKOO_DEPLOY_KEY | app secret key (mk_live_sk_…) — authorizes the deploy |
MUHKOO_APP_ID | the app id |
VITE_MUHKOO_KEY | app publishable key (mk_live_pk_…) — baked into the bundle |
The deploy API
Section titled “The deploy API”If you’re rolling your own deploy, the API is under /api/apps/:appId/hosting,
authorized by Authorization: Bearer <app sk | dev session>:
| Method | Path | Body | Purpose |
|---|---|---|---|
| PUT | /hosting/blob/:sha | raw file bytes | Upload one file; the server verifies sha256(body) === sha and dedups. |
| POST | /hosting/releases | { manifest: { "<path>": "<sha>" } } | Commit a release (validates blobs, enforces quota, flips live). Returns { releaseId, url, bytes, files }. |
| GET | /hosting | — | Site status: url, current release, bytes, history. |
| POST | /hosting/rollback | { releaseId } | Re-point to a prior release. |
| DELETE | /hosting/releases/:releaseId | — | Delete a non-current release from history. |
| DELETE | /hosting | — | Unpublish. |
The manifest maps each file’s path (relative to dist/, forward slashes,
index.html at the root) to its blob sha. index.html is required; unknown
deep-link paths fall back to it (single-page-app routing). Asset files are cached
immutably; index.html revalidates, so a deploy is live immediately.
The platform keeps the last 10 releases per app (the live one is always kept); older releases auto-prune on deploy, and you can delete any non-current release.
Custom domains
Section titled “Custom domains”Serve your app on your own domain — app.yourcompany.com instead of
<slug>.apps.muhkoo.dev. Muhkoo issues and auto-renews the TLS certificate;
you keep your domain at whatever DNS provider you already use, and Muhkoo never
touches your DNS.
-
In the portal, open your app → Custom domains → enter your hostname (e.g.
app.yourcompany.com) → Add domain. -
The portal shows two DNS records. Add them at your DNS provider:
Type Name Value Why CNAME app.yourcompany.comcname.muhkoo.ioRoutes traffic to your app. CNAME _acme-challenge.app.yourcompany.com(shown in portal) One-time; lets Muhkoo issue + auto-renew the cert. -
Muhkoo verifies ownership from those records, then the domain flips to Active and serves your app over HTTPS (usually a few minutes). It only goes live after verification — an unverified domain is never served. The cert renews itself from then on.
Manage domains over the API too, under /api/apps/:appId/hosting/domains
(Authorization: Bearer <app sk | dev session>): POST { hostname } to attach,
GET to list with live status + the records to add, DELETE /:hostname to detach.
- One site per app (the subdomain is the app’s slug, reserved to that app — two apps can never collide on a host).
- The hosted page calls
api.muhkoo.devwith your publishable key from<slug>.apps.muhkoo.dev; the default CORSallowedOrigins: "*"permits it. If you tighten CORS, add the hosting origin. - Rollback is a pointer flip — instant. The last 10 releases stay available to roll back to (or delete); the live release is never pruned.