The moving link is team-first by design: it routes a new join to whoever needs
directs next, so the position whose link was actually clicked earns no entry
reward. Measured on #21 that came to 80 joins in seven days and zero entry
rewards, while the traffic paying for those joins was the owner's.
For a position being deliberately built out, that default is backwards. Any ID
listed in the new directDefaultIds config now behaves as ?direct=1 on its own
/join/<id> link - including every promo tool, flyer QR and downline-builder
entry that resolves through it - without needing the parameter appended.
?direct=0 still forces rotation for a single share, so the team-first behaviour
stays available rather than being replaced.
Config rather than hard-coded, and editable from the admin settings form, so
which positions get this can change without a deploy.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The panel fetched its tool list on DOMContentLoaded — which fires while the
admin page is still showing the login form. The request 401'd, the handler
returned silently, and the "What" select was left permanently empty with no
path back.
Loads from the same post-login block as the coaching radar and messages
panels now, plus a refetch if the select is opened while still empty.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>