diff --git a/messages.js b/messages.js
index 9ab37d1..b0f42a2 100644
--- a/messages.js
+++ b/messages.js
@@ -89,6 +89,16 @@ function verifyChallenge(address, signature) {
saveSessions();
return { token, id };
}
+// Mini App sessions: identity was already proved once (wallet-verified
+// Telegram link), and Telegram re-proves the chat via signed initData — so a
+// session can be minted without a fresh wallet signature. No address attached.
+function mintSession(id) {
+ if (!Number.isInteger(id) || id < 1) return null;
+ const token = crypto.randomBytes(32).toString('hex');
+ sessions.set(token, { address: null, id, expires: Date.now() + SESSION_TTL, via: 'tg' });
+ saveSessions();
+ return token;
+}
function authFromCookie(req) {
const m = /(?:^|;\s*)ctb\.msid=([^;]+)/.exec(req.headers.cookie || '');
if (!m) return null;
@@ -168,4 +178,4 @@ function adminList() {
return getMessages().slice(-300).reverse().map(m => ({ mid: m.mid, fromId: m.fromId, toId: m.toId || null, org: !!m.org, body: m.body, ts: m.ts, readCount: Object.keys(m.read || {}).length }));
}
-module.exports = { init, makeChallenge, verifyChallenge, authFromCookie, sessionCookie, send, inbox, markRead, unreadCount, adminList, ADDR_RE };
+module.exports = { init, makeChallenge, verifyChallenge, mintSession, authFromCookie, sessionCookie, send, inbox, markRead, unreadCount, adminList, ADDR_RE };
diff --git a/public/app.html b/public/app.html
new file mode 100644
index 0000000..b3eea35
--- /dev/null
+++ b/public/app.html
@@ -0,0 +1,30 @@
+
RM Circle
+
+
+
+
+
+
+
Opening your dashboard…
+
Verifying your Telegram link.
+
+
+
One-time setup
+
Link your position first
+
This app shows YOUR live dashboard — payments, team, coaching. To prove a position is yours, link it once with your wallet (a free signature, it can't move funds):
+
1. Open rmcircle.team/my in your browser 2. Enter your member ID → Messages → sign in with your wallet 3. Tap “Connect Telegram” — it deep-links right back to this bot
+
+
+
+
+
Hmm, that didn't verify
+
Could not verify the Telegram launch data.
+
+
+
+
This page opens inside Telegram
+
It's the RM Circle Mini App — open the companion bot in Telegram and tap the ☰ menu button. In a normal browser, use the regular dashboard instead.
+
+
diff --git a/public/app.js b/public/app.js
new file mode 100644
index 0000000..6ba5416
--- /dev/null
+++ b/public/app.js
@@ -0,0 +1,37 @@
+// Telegram Mini App entry: verify initData server-side, then land the linked
+// member on THEIR dashboard with a minted session — zero login. Unlinked
+// users get the one-time wallet-link instructions instead.
+(function () {
+ 'use strict';
+ var tg = window.Telegram && window.Telegram.WebApp;
+ function show(id) {
+ ['st-loading', 'st-unlinked', 'st-error', 'st-notg'].forEach(function (x) {
+ var el = document.getElementById(x); if (el) el.style.display = x === id ? '' : 'none';
+ });
+ }
+ function on(id, fn) { var el = document.getElementById(id); if (el) el.addEventListener('click', fn); }
+ on('btn-open-site', function () {
+ var url = location.origin + '/my';
+ if (tg && tg.openLink) tg.openLink(url); else location.href = url;
+ });
+ on('btn-browse', function () { location.href = '/start'; });
+ on('btn-retry', function () { location.reload(); });
+
+ if (!tg || !tg.initData) { show('st-notg'); return; }
+ // Flag the webview session so tg-app.js activates on every page after this one.
+ try { sessionStorage.setItem('rmcTg', '1'); } catch (e) {}
+ tg.ready();
+ try { tg.expand(); } catch (e) {}
+ try { tg.setHeaderColor('#071421'); tg.setBackgroundColor('#071421'); } catch (e) {}
+
+ fetch('/api/public/tg-webapp-auth', {
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ initData: tg.initData })
+ }).then(function (r) { return r.json(); }).then(function (d) {
+ if (d && d.ok && d.linked) { location.replace('/my/' + d.id); return; }
+ if (d && d.ok) { show('st-unlinked'); return; }
+ var el = document.getElementById('err-detail');
+ if (el && d && d.error) el.textContent = d.error;
+ show('st-error');
+ }).catch(function () { show('st-error'); });
+})();
diff --git a/public/chat.js b/public/chat.js
index d89c9b4..938546c 100644
--- a/public/chat.js
+++ b/public/chat.js
@@ -35,8 +35,8 @@
a:()=>`Great question — and we checked it on-chain, not just in theory. The contract needs no one to keep it running: joins, upgrades, placement and every payout are fully automatic — no button anyone has to press, no expiry. If the creators walked away, lost their keys, or vanished, member payments keep flowing exactly as coded. We also verified that the founder, development and fee wallets are ordinary wallets, not programs — an ordinary wallet always accepts an incoming payment even if its key is lost forever, so a dead admin wallet can't jam a single member payment (at worst the project's own fee sits there uncollected). And the contract holds no stored balance — every payment is delivered in the same transaction. Full write-up in section 6 of rmcircle.team/contract.`},
{k:['pyramid','ponzi','pyramid scheme','ponzi scheme','mlm','recruiting scheme','is this a scheme'],
a:()=>`A pyramid or Ponzi scheme funnels everyone's money to a central company and pays earlier joiners out of later joiners' deposits — and you can't verify any of it. This is the opposite: no company holds the money. A public smart contract on Polygon sends each payment person-to-person in the same transaction it arrives, and you can read the code and every payout yourself on-chain — nothing pooled, nothing hidden, rules that can't be changed. It is a team build, so it takes real effort and carries real crypto risk — not a passive investment. But you don't have to trust anyone; verify it at rmcircle.team/contract. No income is guaranteed.`},
- {k:['telegram bot','connect telegram','payout ping','telegram notification','message my downline','contact my downline','reach my downline'],
- a:()=>`Link your position to our Telegram companion bot: open your dashboard → Messages → sign in with your wallet → tap "Connect Telegram". Once linked you get an instant DM whenever your position catches a payment, team messages reach you natively in Telegram (reply right there to answer), you're pinged when someone joins on your link, and "links" gives you all your invite links. Messaging still follows your matrix lines only — same rules as the site.`},
+ {k:['telegram bot','connect telegram','payout ping','telegram notification','message my downline','contact my downline','reach my downline','mini app','miniapp','telegram app','dashboard in telegram'],
+ a:()=>`Link your position to our Telegram companion bot: open your dashboard → Messages → sign in with your wallet → tap "Connect Telegram". Once linked you get an instant DM whenever your position catches a payment, team messages reach you natively in Telegram (reply right there to answer), you're pinged when someone joins on your link, and "links" gives you all your invite links. Linked members can also tap the bot's ☰ menu button to open the Mini App — your full live dashboard, promo tools, and the Circle Method right inside Telegram, no login needed. Messaging still follows your matrix lines only — same rules as the site.`},
{k:['cash out','cashout','spend my crypto','withdraw','off ramp','off-ramp','gift card','giftcard','get my money out','turn into cash','convert to dollars'],
a:()=>`Three good paths, easiest first: (1) E-gift cards — send POL to CWallet (cwallet.com), swap to a US-dollar token there (their internal swaps are virtually free), and buy gift cards for brands you already use — groceries, gas, Amazon. eGifter (egifter.com) also takes crypto directly. (2) Straight cash-out via a regulated exchange in your country (Coinbase, Kraken…): send, sell, withdraw to your bank. (3) Keep it working — many members leave catches in the wallet to fund their next level. Full guide: Spending what you earn. Honest notes: those are independent custodial services — only move what you're about to spend; taxes may apply where you live; not financial advice.`},
{k:['circle method','recruiting course','how do i recruit','recruiting training','get my two','get your two','how to invite','module 1','lessons'],
diff --git a/public/contract.html b/public/contract.html
index a65d51f..8e709d0 100644
--- a/public/contract.html
+++ b/public/contract.html
@@ -30,4 +30,4 @@
-