MIP-0023: Product expansion — a wait-list, honest promotion, and the easiest way to keep marola's site and backend maintained¶
| Status | Draft |
| Author | Claude Sonnet 5, for M. Hoffmann (request of 2026-09-06: "Mip product expansion, wait-list betalist... How to promote easiest way to maintain and deploy marola for external users marola site and backend updates, use state of the art tooling with agentic") |
| Created | 2026-09-06 |
| Phase | 0 (wait-list can start today, site has no backend) → 1 (promotion of the bot itself needs the Telegram bot working, ARCHITECTURE.md §11) → 3 (bot deploy is the existing Container-App-or-cheaper-alternative design point) |
| Related | ARCHITECTURE.md §5/§11, MIP-0005 (the static site this sits on), MIP-0002 (Telegram bot, Phase 1 — prerequisite for promotion), MIP-0004 (in-bot digest subscriptions, distinct from this pre-launch wait-list), MIP-0017 (agentic dev-tooling survey — §4.3 extends it to the deploy/maintain axis), MIP-0020 (Instagram bot account — a promotion channel §4.2 should link to once that MIP ships), MIP-0020 §5.5 (the daily Instagram pipeline — the same promotion axis, platform-specific; the former MIP-0024 draft was folded into it), MIP-0025 (custom-model work — orthogonal, not a promotion or deploy concern), docs/3-Working-on-the-repo/DEV-FLOW.md, AGENTS.md (cost/deployment-safety and phase-discipline gates) |
| Effort | S for §5.1's default (a GitHub Discussion, zero new code); M if the Formspree-form alternative is chosen instead (one static form + one Pages redirect page, no server); L if a Cloudflare Worker + KV path is chosen (a new tiny stateful component, its own secrets and quota to watch) |
| Gain | user value (a way to say "I want this" before the bot exists); infra/dev-loop (§5.3's maintenance recommendation); cost/ops (every option here is free or near-free by design) |
| Effort vs Gain | cheap win for §5.1 (wait-list) and §5.3 (maintenance tooling) — both buildable now, zero paid resource; §5.2 (promotion) is do when X lands — it needs Phase 1 (a working bot) to be worth doing at all, per AGENTS.md's phase-discipline rule |
| Depends on | Phase 1 (MIP-0002) gates promotion (§4.2), not the wait-list (§4.1) or tooling (§4.3), both usable today; no paid cloud resource anywhere here, per AGENTS.md's cost-safety rule |
| Risk | promoting before Phase 1 ships wastes a hobby project's one good first impression — a "sign up to be told later" link with no bot behind it reads as vaporware; no promotion push should start before MIP-0002 merges |
| Cost so far | — (nothing from this MIP has merged yet) |
1. Summary¶
marola has a real product surface today, the static map (MIP-0005, live at https://h0ffmann.github.io/marola/), and a real gap: no way for an interested visitor to leave contact info for the not-yet-built Telegram bot, no plan for where a hobby launch gets seen, and no written answer for keeping the site and eventual bot maintained without babysitting. This MIP proposes: (1) a wait-list that respects the site's "no cookies, no tracking, zero third-party requests" claim, defaulting to a GitHub Discussion thread over an embedded third-party form; (2) an honest, hobby-scale promotion plan gated on Phase 1 actually shipping; (3) a maintenance/deploy recommendation: GitHub Actions + Pages for the site (already built), Fly.io for the eventual bot, and the already-running Dependabot/scala-steward plus the Claude Code GitHub Action as the "agentic" maintenance layer, instead of a bespoke system.
2. Motivation¶
- The site's copy makes an explicit privacy promise:
app.js:11, "Nothing leaves the browser: no analytics, no cookies", and the footer atapp.js:334, "...and, soon, in the Telegram bot". That "soon" has nowhere for a visitor to register interest today; the maintainer's only signal is asking friends by hand. - The repo already has the free deploy artefact for the site (
site.yml, MIP-0005/§11 Phase 3), but nothing is written down about the equivalent decision for the backend once the bot exists. No cheap option has been evaluated in writing. AGENTS.md's phase-discipline rule says not to promote or scale what isn't built: marola has zero live bot users because the bot doesn't exist. Any "get external users" plan must say, out loud, which part is gated on Phase 1 and which isn't.
3. User-visible change¶
Before: the map's footer says "soon, in the Telegram bot" with no link. A visitor who wants to be told when it ships has no way to say so except messaging the maintainer directly.
After (site copy, once §5.1's default ships):
No cookies, no tracking. The same pipeline answers one beach at a time on the command line
and, soon, in the Telegram bot — want to know when it's live? Leave a note on the wait-list.
Leave a note on the wait-list links out to a GitHub Discussion (Announcements category,
pinned), not an embedded widget. The click is the user's own choice to leave the zero-third-
party-requests page, not a background request the page makes for them. Once the bot exists
(Phase 1), the same Discussion thread's pinned comment is edited to say "it's live: t.me/
4. Data sources and dependencies reviewed¶
4.1 Wait-list options¶
GitHub Discussions sign-up thread (recommended default). A pinned Discussion in this already-public repo; a visitor comments "notify me" or reacts. Cost $0, no new account/secret/ dependency. Privacy: comments are public GitHub data under GitHub's own terms, the same trust boundary a visitor already crosses to read the repo; nothing reaches a third party marola chose. Maintenance: zero, no cron, no server, no library to patch. Downside: needs a free GitHub account and it's a public thread, not a private list. Verified: GitHub Discussions is free on public repos, a long-standing, widely known GitHub feature, not independently re-fetched from a pricing page this session.
Formspree-style hosted form (Buttondown for the email-list variant). An external service POSTs
submissions to the vendor. Buttondown checked live 2026-09-06 (buttondown.com/pricing): free
for up to 100 subscribers, no card required; paid add-ons ($9-79/mo) are opt-in extras not needed
for a bare wait-list. Its exact data-retention/third-party-sharing terms (/legal/privacy) were
not read in full; Open Questions. Formspree's pricing page did not render numeric limits to
this session's fetch (JS-heavy, empty extraction); its widely-cited ~50 submissions/month free
tier is unverified this session, Open Questions. Either service embeds a form, a third-party
request on page load, conflicting with the site's "zero third-party requests" claim unless the
form lives on a separate linked-to page rather than inline on index.html.
Telegram-bot-native /join. Only possible once the bot exists (Phase 1), a /join command
for a small beta cohort. Cost $0 beyond the bot; privacy is the best of all options (no new data
path). Doesn't solve the pre-Phase-1 problem this MIP is about; the natural graduation point
once Phase 1 ships, not a Phase-0 answer.
Cloudflare Worker + KV/D1, a self-hosted sign-up endpoint. A Worker POST target, storing the
signup in KV/D1. Checked live 2026-09-06: both cloudflare.com/products/workers and
workers.cloudflare.com redirected to marketing pages with no numeric free-tier figures in this
session's fetch; the commonly-cited 100k requests/day plus free KV/D1 allowances are
unverified this session, Open Questions. Even if those hold, this is real new infrastructure,
a script, a namespace, an account/token to manage, and the only option that makes "zero
third-party requests" literally false the moment the form loads. Highest effort, least
differentiated gain over the Discussions default.
Pick: GitHub Discussions as the default. No new account/secret/dependency, and its "public thread" trade-off is disclosed in the sign-up copy rather than hidden behind a form that quietly adds a third party. Buttondown is the named fallback for a private list; its verified 100-subscriber free tier covers a hobby launch, but isn't the default since it's still a vendor dependency the Discussions option avoids entirely.
4.2 Promotion channels¶
| Channel | Effort | Likely gain at hobby scale | Prerequisite |
|---|---|---|---|
| README badge/link to the live map | Trivial | Low reach, zero cost, already partly done | None |
| Telegram groups of local swimmers/surfers | Low, post where the audience already is | Highest gain-per-effort; direct, non-spammy if framed as "I built this" | Site only today; wait for Phase 1 to promote the bot |
| A local subreddit (r/florianopolis-style) | Low | Moderate; self-promotion rules vary per subreddit, check before posting | None for the site |
| BetaList | Medium (submission + review wait) | Low-to-moderate, audience skews general-startup, not local-utility | Checked 2026-09-06: submission page needed login/JS, no pricing disclosed, unverified, Open Questions |
| Product Hunt | Medium-high (a launch day, assets) | High-variance visibility; audience is tech-early-adopter, a stretch fit unless framed as the AI-agent story | Checked 2026-09-06: launch page needed login/JS, no requirements disclosed, unverified, Open Questions |
Pick, in order of effort-adjusted gain: README badge (do now, trivial), then local Telegram groups and a relevant subreddit, framed honestly as a hobby project (do once the map alone is worth sharing, and it already is, MIP-0005 is Implemented), then BetaList/Product Hunt only after Phase 1 ships and the wait-list has some names on it, since both are one-shot "launch day" channels better spent on a product that actually replies when someone messages it. An Instagram account (MIP-0020) is a fifth channel in the same "post where the audience already is" spirit as the Telegram groups row above: visual, beach-photo-friendly, and a natural fit once that MIP ships; not designed here.
What must be true first, per AGENTS.md's phase-discipline rule: ARCHITECTURE.md §11 Phase
1 (the Telegram bot actually working): do not spend a BetaList/Product Hunt launch slot on a
"soon" promise. The static site can be shared today; the bot cannot be promoted until it exists.
4.3 Maintain-and-deploy tooling¶
Site (already built, verify only). site.yml builds every 3h and on main pushes touching
site/**/core/**/etc., deploys to GitHub Pages via actions/deploy-pages@v5, confirmed by
reading the workflow file directly (.github/workflows/site.yml), free (GitHub Actions minutes on
a public repo, GitHub Pages hosting). No change proposed here; it already is the "easiest way."
Backend deploy, once Phase 1 ships: Fly.io vs. Hetzner. Any paid host needs AGENTS.md's human
go-ahead before provisioning. Two options, checked live 2026-09-06: Fly.io
(fly.io/docs/about/pricing/) quotes a shared-cpu-1x/256MB machine around $2/mo continuous, or
a $36/yr reservation block (~40% off); whether a perpetual no-card free allowance still exists in
2026 is unverified, Open Questions (a long-polling bot needs one always-on process; webhooks
could scale to zero; polling-vs-webhook is undecided, also Open Questions). Hetzner Cloud
(hetzner.com/cloud/) didn't render numeric prices to this fetch; its cheapest shared-vCPU VMs are
widely cited around €3-4/month but that figure is unverified this session, and Hetzner has no
free tier: plain pay-for-what-you-provision, more OS-patching burden than a managed platform.
Pick: Fly.io as the default recommendation. Cheapest verified always-on option with the least operational surface; explicitly not provisioned by this MIP, a recommendation to revisit with a real go/no-go once Phase 1 ships and traffic is real.
"Agentic" maintenance layer. Already running in this repo, not proposed new:
- scala-steward.yml (weekly Scala/sbt PRs) and .github/dependabot.yml (Actions + the
two Python requirements.txt files), confirmed present by reading both files this session;
just deps-stack already chains their PRs (docs/3-Working-on-the-repo/DEV-FLOW.md §6).
- The Claude Code GitHub Action (code.claude.com/docs/en/github-actions, fetched
2026-09-06): supports a schedule-triggered cron workflow in automation mode (a prompt input,
no @claude mention needed), billed via the repo owner's ANTHROPIC_API_KEY or a
CLAUDE_CODE_OAUTH_TOKEN from an existing subscription; no separate product cost beyond Actions
minutes. This is the "state of the art agentic" piece the request names, and it fits the existing
just/MIP-stack workflow rather than replacing it: a scheduled run could summarize
just deps-stack status or flag a stalled MIP task file, to the workflow log, or (narrowly
--allowedTools-scoped) a comment on a stale PR. Not proposed to merge anything:
.claude/settings.json's permissions.deny already blocks gh pr merge/close project-wide
with no override, matching AGENTS.md's "merging is a human decision" stance.
- OpenTelemetry cost export (AGENTS.md's "heavier option"). Not adopted here; a future
Phase-2-adjacent nicety (agent spend next to marola's own traces).
Recommendation, cheapest-first: keep site.yml as-is (already the answer for the site); add
one new scheduled Claude Code GitHub Action workflow, in automation mode, that reports (does
not act on) the state of open dependency/MIP-stack PRs weekly, the smallest possible "agentic
maintenance" addition, no new secret beyond the ANTHROPIC_API_KEY/CLAUDE_CODE_OAUTH_TOKEN the
maintainer already has for local Claude Code use; defer the Fly.io backend deploy decision itself
until Phase 1 ships, per phase discipline.
5. Design¶
This MIP proposes copy/workflow decisions, not new Scala modules; nothing in core/, local/,
or cli/ changes. Deferred to follow-up implementation PR(s) per the mip skill's
"don't build it in the same change" rule:
site/static/index.html/app.js: one new sentence + link in the existing footer block (app.js:334), pointing at a GitHub Discussion URL. No new JS logic, no new network call from the page itself, a plain<a href>, so "zero third-party requests" survives (a click is the visitor's own navigation, not a request the page makes on load).- One GitHub Discussion, created by the maintainer (a one-time repo-UI action, not code),
pinned, category
Announcements, titled roughly "Wait-list: get notified when the bot ships." - Optionally, a follow-up MIP for the Fly.io bot deploy itself once Phase 1 (MIP-0002) is Accepted or in progress: the actual Dockerfile/fly.toml and webhook-vs-polling decision belongs there, per this repo's "one MIP, one concern" habit.
- Optionally, a follow-up PR adding
.github/workflows/claude-maintenance-report.yml, aschedule-triggered Claude Code GitHub Action in automation mode,--allowedToolsscoped to read-onlygh/just deps-stack statuscalls, writing its summary to the workflow log or a tracking-issue comment, never given merge/close permission, per.claude/settings.json's deny rules.
Nothing here is deterministic scoring or safety-relevant output, so none of it touches
Swimability/Recommender/the reviewer pass.
5.1 Identity & domain strategy¶
The maintainer separately proposed (since .ocean/.sea aren't valid TLDs and marola.ai is
already taken): marola.app.br as the primary domain, marola.dev.br/marola.bot.br as
alternatives, marola.io/marola.bot as global options.
Checked 2026-09-06: IANA's root zone db lists no .ocean or .sea TLD, confirming the premise. An
RDAP query to .bot's own registry endpoint (rdap.nominet.uk/bot/domain/marola.bot, from IANA's
bootstrap file) returned 404, consistent with unregistered, not cross-checked elsewhere. .ai/
.io publish no RDAP endpoint in that bootstrap file (both ccTLDs historically lack public RDAP);
direct/guessed registry RDAP hosts 403'd or failed to resolve. marola.ai's taken status (the
maintainer's own claim) and marola.io's status are unverified this session. Registro.br's
pricing and WHOIS/availability pages are JS-rendered, returning no static content; the cited
R$40/yr .app.br price and marola.app.br/.dev.br/.bot.br availability are unverified;
check registro.br directly before registering anything.
What a domain buys the wait-list: GitHub Pages accepts a custom domain for free, a CNAME
file plus one DNS record at the registrar, no paid GitHub feature, no change to the "no cookies,
no tracking" design. It buys a memorizable URL for §4.2's promotion, nothing more; it doesn't
move the bot closer to shipping. The registrar's annual fee, whichever domain is chosen, is a
small but real recurring cost and, like any spend, a human decision to make and pay for
directly, not something this MIP or an agent provisions.
6. Scoring / safety impact¶
None. No change to Swimability.score, ranking, or any user-facing swim recommendation.
7. Verification plan¶
- Manual: open the new footer link, confirm it lands on the Discussion, confirm no new network request fires on page load (browser devtools).
scripts/site_check.js(MIP-0009 task 2) should still pass unchanged: a static link, not new DOM structure; re-run in the implementation PR to confirm.- If the scheduled Claude Code Action workflow is built: a manual
workflow_dispatchrun, confirm it stays read-only (nogh pr merge/closesucceeds) and the summary is legible. - "Done" for this MIP: Draft merged with the index row added; follow-up implementation PR(s) are
separate, each carrying its own
Cost:/Tested:trailers.
8. Risks, limitations, and honest caveats¶
- A public GitHub Discussion sign-up is not private; anyone can see who signed up; state this in the sign-up copy itself, not just here.
- Promotion (§4.2) before Phase 1 ships risks a bad first impression on a small audience that doesn't get a second launch day; this MIP explicitly gates BetaList/Product Hunt on Phase 1.
- Several external figures in §4/§5.1 could not be confirmed live this session (pricing/quotas for Formspree, Cloudflare Workers, Hetzner, Fly.io, BetaList, Product Hunt, Registro.br, plus marola.ai/marola.io's registration status); each is flagged inline and repeated in §11.
- Fly.io and any domain are design-time picks, not provisioned/purchased resources; no cost incurred; both real go/no-gos are later, human decisions.
9. Alternatives considered¶
- Do nothing (keep asking friends by hand). Free, but no durable signal or scale past the maintainer's own network. Loses because the point was a written, repeatable answer.
- Embed a Formspree/Tally form directly on
index.html. Rejected as default: silently breaks "zero third-party requests" every page load, not just on a click-through; kept as the §4.1 fallback for a private list. - Build a bespoke Cloudflare Worker sign-up form now. Rejected: highest effort/attack surface for a wait-list a free GitHub Discussion already solves with zero new code.
- Launch on Product Hunt/BetaList immediately, wait-list as the "product." Rejected: a poor audience match for "not live yet," burning a one-shot slot on the weakest form of it.
11. Open questions¶
- Exact free-tier figures not confirmed live this session (JS-rendered/empty fetches): Formspree's submission cap, Cloudflare Workers' request/day and KV/D1 allowances, Hetzner's cheapest VM price, Fly.io's no-card-free-tier status (its ~$2/mo shared-cpu-1x price did come through).
- BetaList's and Product Hunt's actual submission cost/requirements: both pages needed login/JS.
- Buttondown's exact data-retention/third-party-sharing terms (
/legal/privacy): only its pricing page was checked. - Webhook vs. long-polling for the eventual bot deploy: affects whether Fly.io scale-to-zero is usable; belongs to the Phase-3 follow-up MIP, not decided here.
- Whether the maintainer wants the wait-list to double as a private list (→ revisit Buttondown).
- marola.ai's taken status and marola.io's registration status:
.ai/.iopublish no RDAP endpoint IANA lists, and guessed registry RDAP hosts 403'd/failed to resolve this session. - Registro.br's actual
.app.br/.dev.br/.bot.brpricing and availability formarola.*: the pricing and WHOIS pages are JS-only and returned no static content this session.
Appendix¶
site/static/app.js:11and:334: existing privacy copy this MIP's footer addition must not contradict..github/workflows/site.yml,.github/dependabot.yml,.github/workflows/scala-steward.yml: read in full this session; the existing tooling this MIP builds on.code.claude.com/docs/en/github-actions, fetched 2026-09-06: schedule-trigger automation mode, billing/permission model.buttondown.com/pricing, fetched 2026-09-06: free-tier cap.data.iana.org/rdap/dns.jsonandiana.org/domains/root/db, fetched 2026-09-06: no.ocean/.seaTLD,.bot's RDAP host;rdap.nominet.uk/bot/domain/marola.bot, fetched 2026-09-06: 404.fly.io/docs/about/pricing/,hetzner.com/cloud/,formspree.io/plans,cloudflare.com/products/workers,betalist.com/submit,producthunt.com/posts/new,registro.br/precos/, its WHOIS tool,rdap.org/domain/marola.{io,ai}: all fetched 2026-09-06; none returned usable numeric pricing/terms or a resolvable RDAP result (JS content, a login wall, 403, or DNS failure); see §11 before relying on them.