Skip to content

Anatomy of a Muhkoo app

Most Muhkoo apps are the same shape: a frontend talking to one Client, with identity at the bottom, data in the middle, and realtime on top. This page is the mental model — what the layers are, how they depend on each other, and the order to build them in. The reference has the exhaustive API; this is the map.

Everything goes through a single Client. You construct it once with your app key, and every namespace hangs off it:

import { Client } from "@muhkoo/connect";
const client = new Client({ apiKey: "mk_live_pk_…" });
// baseUrl defaults to the hosted accelerator (https://api.muhkoo.dev)
client.auth // identity: who the user is
client.kv // per-user encrypted key/value
client.db // app-scoped shared tables
client.storage // encrypted files
client.message // realtime pub/sub + E2E direct messages
client.space // group channels with history
client.agents // manage server-side AI agents
client.functions // manage serverless functions

There’s no separate auth SDK, storage SDK, and realtime SDK to wire together. One sign-in flows through all of them: once the user is authenticated, every namespace is ready.

A Muhkoo app stacks in three layers, each depending on the one below.

  1. Identityclient.auth. The user signs in (hosted “Continue with Muhkoo”, or embedded zero-knowledge), which establishes a session and unlocks the identity (the on-device key material). Almost everything above needs the session; encrypted reads/writes need the unlocked identity.

  2. Data — where state lives. Pick per the storage decision guide:

    • client.kv — per-user, encrypted-at-rest key/value (the server only sees ciphertext).
    • client.db — app-scoped, queryable, shared tables (schema defined in the portal).
    • client.storage — encrypted files (live in a Space).
  3. Realtime — collaboration on top of data.

Above these three sit the server-side primitives — serverless functions and programmable agents — your own code and AI that run on the accelerator and act on the layers below.

A small team app — say a shared task board with a chat channel — wires up like this:

const client = new Client({ apiKey: import.meta.env.MUHKOO_KEY });
// 1. Identity — hosted sign-in, no login UI to build.
if (client.auth.hosted.isCallback()) {
await client.auth.hosted.handleCallback();
}
if (!client.auth.zk.identity) {
await client.auth.hosted.login({ appId, redirectUri });
}
// 2a. Shared, queryable data → the database (defined in the portal).
const tasks = client.db.table<Task>("tasks");
await tasks.insert({ title: "Ship docs", done: false });
const open = await tasks.query({ where: { done: false } });
// 2b. Private per-user data → encrypted KV.
await client.kv.set("prefs", "ui", { theme: "dark" });
// 3. Realtime — a group channel with history.
const channel = await client.space.joinChannel("general");
channel.on("message", (m) => render(m));
await channel.send({ text: "board updated" });

Private user state (prefs, drafts) goes in encrypted KV; shared structured state (the tasks everyone sees) goes in the database; the live conversation rides a Space. Same client, one session, three different storage semantics chosen deliberately.

A predictable sequence that avoids rework:

  1. Create the app & keys — in the portal or with muhkoo provision (CLI guide). You get a publishable key for the client and a secret key for deploys.

  2. Wire auth first — get a real signed-in user before anything else, since the data layers need the session. Start with hosted auth unless you need the login UX fully in-app.

  3. Model your data — decide per piece of state: encrypted KV, the database, or files. See Choosing storage. Define database tables in the portal before calling client.db.

  4. Add realtime where users collaborate — direct messages or group channels.

  5. Add server-side logic if needed — functions for your own code, agents for AI in a Space. Both are paid features.

  6. Ship — build the client and deploy it to hosting. Use a test environment to iterate without touching production.