An on-chain social room built on Thebes Protocol: a Motoko backend that holds rooms, messages, member profiles and the roster, and a React frontend served as certified assets.
The property this example proves: an accountable conversation. Every kept
message is attributed and strictly ordered; deletion leaves a tombstone (the
record that something was said is never quietly erased); trimming stays on the
books (kept + trimmed = everything ever sent); and the anti-spam cooldown is
enforced by the contract, not the client. The public oracle
(invariantReportView) recomputes those laws for every room on every read.
Live demo: https://memphis.mercaturaforum.com/_/raw/186131445741167/index.html
frontend (React + Vite + Tailwind) → chat backend (Motoko)
@thebes/sdk ── boundary client mo:thebes-lib ── Admin / MemphisAuth / Users / Pagination
Memphis passkey gate messages · profiles · roster
- frontend/ uses
@thebes/sdkfor the boundary client, typed query/update calls, React hooks, and the Memphis passkey gate. The SDK is vendored underfrontend/vendor/@thebes/sdkand resolved as a local dependency (upstream source of truth:thebes-sdk). - motoko/ uses
thebes-libforAdmin(controller-gated operations),MemphisAuth(passkey session verification),Users(profiles), andPagination; the room logic lives inmain.mo. The library is vendored undermotoko/thebes-liband resolved as a local Mops dependency.
Both halves are self-contained: the repository builds with no external Git or Mops toolkit pins. The frontend asset-canister wasm is the one artifact fetched at deploy time (see Deploy).
| Method | Kind | Purpose |
|---|---|---|
register |
update | Create or update the caller's display name. |
setMyAvatar / setMyAvatarOrTrap |
update | Store the caller's media-contract avatar path; the OrTrap form traps on a failed guard so the client never silently ignores an error. |
myProfile / profileOf / userCount |
query | Read profiles and the member count. |
roster / rosterView |
query | Paginated member roster (offset, limit). |
post |
update | Post a message as the transport sender. |
postAs |
update | Post under a Memphis signed-in per-app principal. |
recent |
query | The last N messages. |
seedDemo |
update | Populate demo members + messages on an empty room. |
claimOwner / addAdmin / setPaused |
update | Ownership and admin surface (from thebes-lib's Admin). |
Messages are an append-only, trimmed log; the roster is paginated via
thebes-lib's Pagination. Avatar images live on a separate media contract — the
backend stores only the returned path string, never image bytes.
- Motoko compiler 1.4.1.
mops installfetches the pinned compiler to~/.cache/mops/moc/1.4.1/moc(macOS:~/Library/Caches/mops/moc/1.4.1/moc). Use that binary — themocon a defaultPATHmay be a different version, or Qt's unrelated Meta-Object Compiler. - Node 18+ and Mops for the two builds.
thebes-deployto deploy. The prebuilt binary is Linux x86-64; on other platforms build it from the release source bundle (cargo build --release -p thebes-deploy).
# Frontend
cd frontend
npm install # resolves the vendored @thebes/sdk
npm run dev # sync-sdk copies the browser runtimes into public/, then Vite serves
# Backend (compile-check)
cd ../motoko
mops install # resolves the vendored thebes-lib + the pinned compiler
"$(ls "$HOME/.cache/mops/moc/1.4.1/moc" "$HOME/Library/Caches/mops/moc/1.4.1/moc" 2>/dev/null | head -1)" --check $(mops sources) main.mothebes.toml describes the deploy. The [networks.wan].validators array ships
pre-filled with the current cluster endpoints — run thebes-deploy init to refresh
them.
Deploying your own copy? The committed
cidvalues pin the live catalog deployment (that's what the demo links serve — only its controller can upgrade it). Before your first deploy, setcid = "auto"on each canister: the deploy allocates fresh canisters you control and writes their ids back into the manifest.
thebes-deploy identity new me # one-time local signing identity
thebes-deploy deploy chat # build + install + verify → prints the backend cidThe frontend installs an asset canister, then uploads your built bundle. Fetch the
asset-canister wasm once (it is referenced by thebes.toml as asset_canister.wasm):
curl -L -o asset_canister.wasm \
https://github.com/Mercatura-Forum/Thebes-Protocol-/releases/download/asset-canister-v0.1.0/asset_canister.wasmBuild the bundle and point it at your backend cid (the frontend reads
window.CHAT_CID at runtime), then deploy:
cd frontend && npm run build && cd ..
# inject the backend cid from step 1 into the built page:
sed -i 's#<head>#<head><script>window.CHAT_CID=YOUR_CHAT_CID;</script>#' frontend/dist/index.html
thebes-deploy deploy web # install asset canister + upload bundle + verifyThe deploy prints the live URL:
https://memphis.mercaturaforum.com/_/raw/<web-cid>/index.html.
Member avatars are served by a separate media canister via
window.MEDIA_CID. It is optional — without one, members render without avatar images.
For a machine-readable deploy contract, see AGENTS.md.
Apache-2.0. See LICENSE.