mirror of
https://github.com/profullstack/agentbbs.git
synced 2026-08-13 14:27:27 +00:00
Rebrand paid tier to $99 Founding Lifetime Member
Replace the $10 "Premium" pitch with a $99 Founding Lifetime Member offer (first 1,000 accounts) and list every bonus feature in the join@ upgrade prompt: personal email + webmail, custom domains, Tor access, and lifetime locked-in pricing. - payments: PremiumPriceLabel/default amount $10 -> $99, add FoundingCap - join@ offer + already-paid welcome + gated-route upsell copy - README + interface doc comments Price stays env-overridable via AGENTBBS_PREMIUM_AMOUNT. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
52b06d8cfe
commit
2a9d841ddb
4 changed files with 30 additions and 19 deletions
|
|
@ -13,9 +13,10 @@ Just two SSH front doors: **`join@`** to onboard a new key, then
|
||||||
domains are all reached from there.
|
domains are all reached from there.
|
||||||
|
|
||||||
**Membership:** a verified-email account is **free** — you get a personal Docker
|
**Membership:** a verified-email account is **free** — you get a personal Docker
|
||||||
pod and a homepage at `https://bbs.profullstack.com/~name`. **Premium** ($10
|
pod and a homepage at `https://bbs.profullstack.com/~name`. **Founding Lifetime
|
||||||
one-time, lifetime) adds a personal `name@bbs.profullstack.com` email (via
|
Member** ($99 one-time, first 1,000 accounts only) adds, for life: a personal
|
||||||
forwardemail.net) and custom domains.
|
`name@bbs.profullstack.com` email + webmail (via forwardemail.net), custom
|
||||||
|
domains, and Tor access (`ssh tor@` — fetch URLs & join IRC over Tor).
|
||||||
|
|
||||||
No browser, no install, no client download. The BBS is a hub of hot-swappable
|
No browser, no install, no client download. The BBS is a hub of hot-swappable
|
||||||
plugins around one shared account system; the full product plan is in
|
plugins around one shared account system; the full product plan is in
|
||||||
|
|
@ -137,7 +138,7 @@ server. Full details: [`docs/irc.md`](docs/irc.md).
|
||||||
- **Store** (`internal/store`): SQLite behind an interface (Postgres later is a
|
- **Store** (`internal/store`): SQLite behind an interface (Postgres later is a
|
||||||
driver swap). Users, sessions, scores, pod subscriptions.
|
driver swap). Users, sessions, scores, pod subscriptions.
|
||||||
- **Payments** (`internal/payments`): CoinPay REST API (coinpayportal.com) for
|
- **Payments** (`internal/payments`): CoinPay REST API (coinpayportal.com) for
|
||||||
the $10 lifetime membership + HMAC payment references; manual grant for ops.
|
the $99 Founding Lifetime membership + HMAC payment references; manual grant for ops.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
// ssh bbs@host the BBS hub, guests welcome (play@/guest@ are aliases)
|
// ssh bbs@host the BBS hub, guests welcome (play@/guest@ are aliases)
|
||||||
// ssh <name>@host the hub as a member/agent (SSH key required)
|
// ssh <name>@host the hub as a member/agent (SSH key required)
|
||||||
// ssh join@host onboarding: registers your key, confirms your email with an
|
// ssh join@host onboarding: registers your key, confirms your email with an
|
||||||
// emailed code, then offers $10 lifetime Premium (CoinPay)
|
// emailed code, then offers $99 Founding Lifetime (CoinPay)
|
||||||
// ssh pod@host your personal Linux pod — free for verified members
|
// ssh pod@host your personal Linux pod — free for verified members
|
||||||
// ssh domain@host point your own domain at your homepage (Premium; add/rm/list)
|
// ssh domain@host point your own domain at your homepage (Premium; add/rm/list)
|
||||||
// ssh admin@host the operator admin console ($AGENTBBS_ADMINS only)
|
// ssh admin@host the operator admin console ($AGENTBBS_ADMINS only)
|
||||||
|
|
@ -372,7 +372,7 @@ func readLine(s ssh.Session, in *bufio.Reader) (string, error) {
|
||||||
|
|
||||||
// handleJoin runs onboarding interactively in one SSH session: register the
|
// handleJoin runs onboarding interactively in one SSH session: register the
|
||||||
// visitor's key, confirm their email with a code we email them, then offer the
|
// visitor's key, confirm their email with a code we email them, then offer the
|
||||||
// $10 lifetime Premium membership (CoinPay). It then disconnects.
|
// $99 Founding Lifetime membership (CoinPay). It then disconnects.
|
||||||
func (a *app) handleJoin(s ssh.Session) {
|
func (a *app) handleJoin(s ssh.Session) {
|
||||||
fp := auth.Fingerprint(s.PublicKey())
|
fp := auth.Fingerprint(s.PublicKey())
|
||||||
if fp == "" {
|
if fp == "" {
|
||||||
|
|
@ -432,7 +432,7 @@ func (a *app) handleJoin(s ssh.Session) {
|
||||||
" homepage https://" + a.host + "/~" + u.Name,
|
" homepage https://" + a.host + "/~" + u.Name,
|
||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
// 2) Premium ($10 lifetime): personal @host email + custom domains.
|
// 2) Founding Lifetime ($99 one-time): personal @host email + custom domains.
|
||||||
a.offerPremium(s, &u)
|
a.offerPremium(s, &u)
|
||||||
_ = s.Exit(0)
|
_ = s.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
@ -577,7 +577,7 @@ func (a *app) ensurePremium(u *store.User) bool {
|
||||||
func (a *app) showPremiumWelcome(s ssh.Session, u store.User) {
|
func (a *app) showPremiumWelcome(s ssh.Session, u store.User) {
|
||||||
lines := []string{
|
lines := []string{
|
||||||
"",
|
"",
|
||||||
" ★ Premium — thanks! Your perks:",
|
" ★ Founding Lifetime Member — thanks! Your perks:",
|
||||||
"",
|
"",
|
||||||
" email " + a.fe.Address(u.Name),
|
" email " + a.fe.Address(u.Name),
|
||||||
" forwards " + u.Email,
|
" forwards " + u.Email,
|
||||||
|
|
@ -592,7 +592,7 @@ func (a *app) showPremiumWelcome(s ssh.Session, u store.User) {
|
||||||
wish.Println(s, strings.Join(lines, "\n"))
|
wish.Println(s, strings.Join(lines, "\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// offerPremium pitches the $10 lifetime membership — a personal @host email and
|
// offerPremium pitches the $99 Founding Lifetime membership — a personal @host email and
|
||||||
// custom domains. When CoinPay can mint a charge in-session it shows the exact
|
// custom domains. When CoinPay can mint a charge in-session it shows the exact
|
||||||
// amount and deposit address; otherwise it falls back to a pay command.
|
// amount and deposit address; otherwise it falls back to a pay command.
|
||||||
// Non-blocking: the member pays out of band and perks unlock on their next
|
// Non-blocking: the member pays out of band and perks unlock on their next
|
||||||
|
|
@ -607,9 +607,15 @@ func (a *app) offerPremium(s ssh.Session, u *store.User) {
|
||||||
|
|
||||||
lines := []string{
|
lines := []string{
|
||||||
"",
|
"",
|
||||||
" Upgrade to Premium — " + payments.PremiumPriceLabel + ", one-time:",
|
" ★ Founding Lifetime Member — $" + payments.PremiumAmount() + ", one-time",
|
||||||
" • your own email " + a.fe.Address(u.Name) + " (forwards to you)",
|
" Only the first " + payments.FoundingCap + " accounts. Pay once, keep it for life.",
|
||||||
|
"",
|
||||||
|
" Everything in your free membership stays free — founding adds these",
|
||||||
|
" bonus features, forever:",
|
||||||
|
" • your own email " + a.fe.Address(u.Name) + " (forwards to you, webmail included)",
|
||||||
" • custom domains point yourdomain.com at your homepage",
|
" • custom domains point yourdomain.com at your homepage",
|
||||||
|
" • Tor access ssh tor@" + a.host + " — fetch URLs & join IRC over Tor",
|
||||||
|
" • locked-in price founding rate is yours for life — never renew, never pay again",
|
||||||
"",
|
"",
|
||||||
}
|
}
|
||||||
if c, ok, err := payments.CreatePremiumCharge(ref); ok && err == nil {
|
if c, ok, err := payments.CreatePremiumCharge(ref); ok && err == nil {
|
||||||
|
|
@ -739,7 +745,7 @@ func (a *app) handleDomain(s ssh.Session) {
|
||||||
_ = s.Exit(1)
|
_ = s.Exit(1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Custom domains are a Premium perk ($10 lifetime). ensurePremium also
|
// Custom domains are a Founding Lifetime perk ($99 one-time). ensurePremium also
|
||||||
// catches a payment that settled since their last visit.
|
// catches a payment that settled since their last visit.
|
||||||
if !a.ensurePremium(&u) {
|
if !a.ensurePremium(&u) {
|
||||||
wish.Println(s, strings.Join([]string{
|
wish.Println(s, strings.Join([]string{
|
||||||
|
|
@ -883,7 +889,7 @@ func (a *app) torMember(s ssh.Session, route string) (store.User, bool) {
|
||||||
return store.User{}, false
|
return store.User{}, false
|
||||||
}
|
}
|
||||||
if !a.ensurePremium(&u) {
|
if !a.ensurePremium(&u) {
|
||||||
wish.Println(s, " "+route+" is a Premium feature ($10 lifetime). Upgrade: ssh join@"+a.host)
|
wish.Println(s, " "+route+" is a Founding Lifetime Member feature ($99 one-time, lifetime). Upgrade: ssh join@"+a.host)
|
||||||
_ = s.Exit(1)
|
_ = s.Exit(1)
|
||||||
return store.User{}, false
|
return store.User{}, false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Package payments gates the one-time $10 lifetime Premium membership on
|
// Package payments gates the one-time $99 Founding Lifetime membership on
|
||||||
// CoinPay (coinpayportal.com). It talks to the CoinPay REST API directly over
|
// CoinPay (coinpayportal.com). It talks to the CoinPay REST API directly over
|
||||||
// HTTP — no `coinpay` CLI needs to be installed on the host.
|
// HTTP — no `coinpay` CLI needs to be installed on the host.
|
||||||
//
|
//
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
// COINPAY_API_KEY Bearer key for the CoinPay API (cp_live_…)
|
// COINPAY_API_KEY Bearer key for the CoinPay API (cp_live_…)
|
||||||
// AGENTBBS_COINPAY_MERCHANT_ID merchant/business id payments are created under
|
// AGENTBBS_COINPAY_MERCHANT_ID merchant/business id payments are created under
|
||||||
// AGENTBBS_COINPAY_API_URL API base (default https://coinpayportal.com/api)
|
// AGENTBBS_COINPAY_API_URL API base (default https://coinpayportal.com/api)
|
||||||
// AGENTBBS_PREMIUM_AMOUNT fiat amount (default 10)
|
// AGENTBBS_PREMIUM_AMOUNT fiat amount (default 99)
|
||||||
// AGENTBBS_PREMIUM_CURRENCY fiat currency (default USD)
|
// AGENTBBS_PREMIUM_CURRENCY fiat currency (default USD)
|
||||||
// AGENTBBS_PREMIUM_BLOCKCHAIN settlement chain (default eth)
|
// AGENTBBS_PREMIUM_BLOCKCHAIN settlement chain (default eth)
|
||||||
//
|
//
|
||||||
|
|
@ -35,11 +35,15 @@ const PodTerm = 31 * 24 * time.Hour
|
||||||
|
|
||||||
// PremiumPriceLabel is the human-readable price for the one-time lifetime
|
// PremiumPriceLabel is the human-readable price for the one-time lifetime
|
||||||
// membership offered at join@.
|
// membership offered at join@.
|
||||||
const PremiumPriceLabel = "$10 (lifetime)"
|
const PremiumPriceLabel = "$99 (lifetime)"
|
||||||
|
|
||||||
|
// FoundingCap is the marketing cap on Founding Lifetime memberships — the offer
|
||||||
|
// is pitched as available to the first this-many accounts.
|
||||||
|
const FoundingCap = "1,000"
|
||||||
|
|
||||||
// Premium charge parameters — overridable via env so the offer can change
|
// Premium charge parameters — overridable via env so the offer can change
|
||||||
// without a rebuild.
|
// without a rebuild.
|
||||||
func PremiumAmount() string { return envOr("AGENTBBS_PREMIUM_AMOUNT", "10") }
|
func PremiumAmount() string { return envOr("AGENTBBS_PREMIUM_AMOUNT", "99") }
|
||||||
func PremiumCurrency() string { return envOr("AGENTBBS_PREMIUM_CURRENCY", "USD") }
|
func PremiumCurrency() string { return envOr("AGENTBBS_PREMIUM_CURRENCY", "USD") }
|
||||||
func PremiumBlockchain() string { return envOr("AGENTBBS_PREMIUM_BLOCKCHAIN", "eth") }
|
func PremiumBlockchain() string { return envOr("AGENTBBS_PREMIUM_BLOCKCHAIN", "eth") }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,8 +82,8 @@ type Store interface {
|
||||||
// SetPremiumPayment records the CoinPay payment id of a pending premium
|
// SetPremiumPayment records the CoinPay payment id of a pending premium
|
||||||
// charge so a later visit can verify whether it settled.
|
// charge so a later visit can verify whether it settled.
|
||||||
SetPremiumPayment(userID int64, payID string) error
|
SetPremiumPayment(userID int64, payID string) error
|
||||||
// GrantPremium marks the account as a lifetime premium member (the $10
|
// GrantPremium marks the account as a lifetime premium member (the $99
|
||||||
// one-time membership), recording the CoinPay payment reference. Idempotent.
|
// Founding Lifetime membership), recording the CoinPay payment reference. Idempotent.
|
||||||
GrantPremium(userID int64, paymentRef string) error
|
GrantPremium(userID int64, paymentRef string) error
|
||||||
|
|
||||||
RecordSession(userID int64, username, remote, route string) (int64, error)
|
RecordSession(userID int64, username, remote, route string) (int64, error)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue