nav-dash.js on all header pages: a top-right My Dashboard button when the
visitor is known (msg session from wallet sign-in or the Mini App bridge is
authoritative; else the last dashboard opened on the device). Prospects with
no identity never see it.
recordEvent was silently dropping the new engaged + ctb-offer-* events
(whitelist); now recorded per source, with admin funnel tiles for both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- messages.js: challenge/personal_sign/recover auth (vendored pinned
js-sha3 0.9.3 + noble-secp256k1 1.7.1, server-side only; self-tested
positive + tamper cases), 30d HttpOnly sessions, message store on the
volume, matrix-line permissions (your downline direct or broadcast, your
upline chain - nothing else, so spam is impossible by construction),
daily rate limits (30 direct / 3 broadcasts), 1500-char plain text
- chain.js: memberIdByAccount (wallet -> position for sign-in)
- API: msg-challenge/-verify/-me/-inbox/-send/-read public + msg-unread
(count only, no auth) + admin/messages (full visibility, disclosed to
members in the UI)
- Dashboard: Messages card with unread bell, one-tap wallet sign-in,
inbox with auto-read, compose with to-ID or whole-team broadcast
- Admin: Member Messages review table
- Chatbot canned answer + AI system prompt updated
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every coaching output now ends by teaching the recipient how to teach:
point them at the Coach Your Team panel on THEIR own dashboard and tell
them to run the same play for their two.
- Member dashboard dCoach footer: "you're not coaching two people - you're
teaching two coaches"; easy-win rows add "show them how you spotted it"
- Admin Coaching Radar: teach-forward rule footer
- AI chat system prompt: COACHING DOCTRINE section so chat answers frame
guidance teach-forward
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- chain.js getCoachingScan(rootId): classifies everyone below a root into
atRisk (POL forming they can't catch - corrected bought-level rule),
rollForward (qualified-at-Scintilla with entry rewards covering Ascensus),
and oneAway (1/2 directs)
- Admin: GET /api/admin/coaching?root= (name-decorated) + "Coaching Radar"
panel with tiered who/what-to-say/POL-at-stake rows, auto-loaded
- Member dashboards: memberPublic now returns .coach scoped to the member's
own leg; new "Coach your team" card shows the same triage so every member
coaches their own team - computed live, no snapshots or cron needed
- Chatbot canned answer + AI system prompt updated to describe the panel
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- chain.js getOrgShare now returns genCounts (breadth-first members per
generation below the root)
- Admin "Your Organization vs. the Network" panel renders a Gen 1..N bar list
with fill-vs-capacity (2^n slots)
- Member dashboard "Your team" card gains a Team Depth section computed from
the subtree client-side, labeling each generation with the level whose
upgrade pays that position (gen D pays at the level D+1 buy)
- AI chat system prompt updated to describe the new dashboard section
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Self-enroll pageviews now tracked as the "joinnow" event (track.js loaded
on the page; server whitelist + admin tiles/columns extended)
- A confirmed on-chain registration in the wallet-connect flow now auto-posts
to the submit-id API, so every self-enroll join records a submission with
source + clickid attribution and fires the purchase event and BeMob postback
with no manual ID entry
- Optional name/handle field on the join page feeds the submission and the
team Telegram alert
- Admin Traffic panel: Join-now views tile, Join-now -> Confirmed conversion,
and a per-source Confirmed column for campaign-level ROI reading
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New chain.getOrgRouting(rootId, ownerIds) simulates the contract's
_payUpline routing for every member below the org root on their NEXT
upgrade, classifying where the POL lands: your positions, a teammate
in-org (healthy), an outsider above the org (true LEAK), or fees.
Surfaced on /api/admin/income and rendered as a panel in "My Positions
— Income": 4-stat summary + expandable list of payments escaping the
org. Distinguishes real leaks (money leaving the team) from teammates
earning — which the old upgrade-needs alert conflated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New chain.getOrgShare(rootId) + /api/admin/org-share endpoint compute an
org's share of the whole contract by both member count and total POL
paid, from in-memory state. Admin card (default root 21, saved to
config.orgRootId, auto-loads) shows the two headline percentages, a bar,
and the member/POL breakdown. Verified: #21 org = 51.1% of members,
49.5% of POL.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New owner upgrade watcher: every 5 min it checks config.ownerIds against
the chain state and, when a leg member is ONE upgrade from paying a
position that isn't eligible yet (below the required level, or not
qualified), emails config.ownerAlertEmail + pings Telegram — "upgrade
#24 to Fabrica, #61 is one upgrade from paying you 2,486 POL." Deduped
per (position, level) in owner-alerts.json, re-fires if the situation
recurs. Same warning renders inline in the admin "My Positions" panel
(upgradeNeeds in the income endpoint). New ownerAlertEmail settings
field. Detection logic unit-tested.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Aggregates every on-chain payment received by a set of owned IDs
(defaults to 21,24,25, saved to config.ownerIds and prefilled/auto-loaded
next time). Shows grand total + per-position totals and a merged,
newest-first ledger (to / from / entry-or-upgrade level / amount) via a
focused chain.getIncome read and an admin-gated /api/admin/income
endpoint. Verified: 5,064.43 POL across 21/24/25, 11 payments.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Prospects who clicked a member's shared /my link landed on a bare stats
dashboard with no explanation of the program. New /join/<id> page carries the
full bridge-page story (video, strategy, matrix, live on-chain payout proof)
personalized to one sponsor: their live position stats as proof, their ID
pinned on the dApp join button, and the submit-your-ID form locked to them
(source invite-<id>). Dashboard share buttons + QR codes now hand out the
invite page; /my stays the member dashboard. Invalid/unregistered invite ids
fall back to the /start rotation. Tracked as new 'join' event with an admin
traffic column.
Also: the chain indexer now auto-increments a rotation sponsor's directs when
a registration names them as referrer (same op as the admin ⊕), announcing it
on Telegram. At 2/2 it alerts to Qualify instead of auto-rotating — moving
the team focus stays a human decision.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two leg-build members submitted the /start form thinking it was required,
polluting rotation bookkeeping. Rather than two forms, the server now
classifies every submission by on-chain truth: referrer == active
rotation sponsor -> "rotation" (Telegram says +1 direct, add to queue);
anyone else -> "leg" (correct sponsor credited, "no rotation action
needed"). The member dashboard gains its own "Just joined under this
position?" form so leg builders have a proper landing spot; /start
feedback explains each path to the submitter; admin submissions table
shows a rotation/leg chip. Verified against live data: ID 65 claiming
sponsor 36 correctly classified leg under #62.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tree nodes now carry referrerId, so positions placed by spillover (their
referrer differs from their matrix parent) get a teal "spillover · ref
#N" tag on cards in both the member dashboard and admin matrix, plus a
legend entry. When a member is unqualified but has spillover positions
under them — the classic "why do I have 2 under me but no checkmark?"
state — a callout explains that spillover grows the matrix but only
members joining with their own ID count toward 2/2, and points them at
their share link.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- "Your team" now covers the member's FULL leg with click-to-drill
pyramid, breadcrumb, and a list-view toggle (mirrors the admin matrix).
- Legend under both the member tree and admin matrix explaining the
qualified checkmark, tier badges, and downline rollup.
- "Share this position" card: while under 2/2 it's a personal recruiting
tool — per-member QR (vendored qrcode-generator, CSP-safe), copy link,
and a join CTA carrying the member's ID as sponsor. At 2/2 the page
automatically retires the link and redirects sharing to the team
rotator, so personal referrals can't undermine the moving-link
strategy.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Gold "★ Qualified" pill in the member-dashboard header (or an amber
"N more directs to qualify" pill), and a gold ✓ corner medallion on
every qualified position card in both the member tree and admin pyramid.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each tree node now carries a rollup of its subtree — members below and
their combined earned POL — shown as a footer line on pyramid cards and
inline in the list view.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The contract books income atLevel = the level the payer upgraded OUT of
(upgrade N->N+1 records atLevel=N), so an Ascensus upgrade pass-up was
displaying as "Scintilla". Income rows are now classified against the
contract's getAllCosts() tables — upgrade pass-ups arrive at full
upgrade cost, entry referral rewards at slot cost minus the 5% admin fee
— and display "Upgrade to Ascensus" / "Entry — Scintilla" in the admin
income table, public feed, and toasts. Stale snapshot rows re-classify
via a forced re-snapshot on boot.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The name in the Sponsor Queue is now a click-to-rename control (pencil
appears on hover), using the existing PATCH endpoint; server trims and
caps the name.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New config teamAlertEmail: the same team-gated on-chain events that post
to Telegram (registration, upgrade, payout at/below teamRootId) also go
to this address via SendGrid, so deep-leg activity surfaces even away
from Telegram.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
When the chain tail sees a payout to a member whose sponsor record has a
contact email, the site emails them the amount, source, and Polygonscan
link (plain text — deliverability). SendGrid key is pasted in a new
admin panel (stored in the data volume, like the OpenRouter key) or set
via SENDGRID_API_KEY. From address defaults to no-reply@mybrandedvoice.com
— the domain SendGrid is DKIM-authenticated for — and is editable in the
same panel. Not gated on teamRootId; any member with an email on file.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sponsors can carry an optional contact email (Add Sponsor form, ✉ inline
edit in the queue, shown under the name). New config teamRootId: any NEW
on-chain event at or below that member ID — registration, upgrade, or
payout seen by the live tail — posts to the Telegram group topic, with
the tx link and the sponsor's contact email when one is on file.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Matrix View now defaults to a generation-by-generation pyramid (root + 3
generations of position cards, empty placements shown as dashed open
slots). Clicking a position re-roots the pyramid there; a breadcrumb
navigates back up. The collapsible list stays behind a toggle. Tree
payload now carries explicit left/right children so an only-child lands
on the correct side.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New chain.js reads the RM Circle contract (0x33Bd…2DAF, Polygon) via free
public RPCs — no API keys. Daily snapshot rebuilds complete payout history
from getIncomeHistory (log providers prune old history), and a 60s
eth_getLogs tail catches new payouts with tx hashes, upgrade context, and
passed-over upline IDs.
- Bridge page: "Live payment proof" feed + timed toast pop-ups for payouts
seen in the last 15 min, every row linking to Polygonscan.
- /start: same toasts; ID submissions are now verified against the contract
(result shown to the member, in Telegram notify, and in admin).
- Admin: on-chain member lookup (lineage to root, directs, matrix children,
full income history) and a collapsible full matrix tree view.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Start page step 6 is now a bold gold-highlighted "Submit your NEW RM
Circle ID" form with larger step numbers throughout; captures the
sponsor the visitor was shown at join time.
- POST /api/public/submit-id: validates numeric ID, dedupes, stores on
the volume, records a per-source "purchase" analytics event, fires a
BeMob postback (txid=purchase-<clickid>) to close paid campaigns, and
posts the new ID + sponsor + source to the configured Telegram chat
(MB Hermes pattern). Rate-limited.
- Admin: Member ID Submissions table, "Confirmed joins" tile, and
Telegram bot token / chat ID settings.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- track.js stores ?clickid= (or ?cid=) for the session; rides through
bridge -> start navigation alongside the existing source attribution.
- Join click (the conversion) sends the clickid; server fires a
fire-and-forget S2S postback to the admin-configured BeMob postback
URL (cid + txid=join-<clickid>, payout 0) and counts successes per
source in analytics.
- New admin setting: BeMob postback URL (server-side only, not exposed
in public config).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Key is stored on the persistent volume (0600, never in git), read
dynamically per request, maskable status only in admin state, and
clearable to fall back to built-in answers. OPENROUTER_API_KEY env var
still takes precedence if ever set.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Videos loudness-normalized to -16 LUFS (originals were ~-33 LUFS).
- Static file serving rewritten to stream with HTTP Range support so
video seeking works; .mp4/.webm MIME types added.
- Training nav links on homepage and /start, plus a first-time callout
on /start; training page views tracked per source in admin analytics.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- track.js captures first-touch source per session (utm_source/src param
or external referrer domain) and logs bridge/start page views.
- Join clicks now carry the source; all events aggregate per-source in
data/analytics.json on the persistent volume.
- Admin: new Traffic & Conversions card with funnel totals (bridge ->
start -> join click rates) and a per-source breakdown table.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Admin: Level column is now a dropdown of the 8 premium levels (Scintilla
through Corona), saving immediately via PATCH; server validates levels.
- Start page: RM Circle Team Build Roadmap graphic above the fold with
Crypto Team Build Network attribution (compressed to 198KB WebP).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>