Files
summercms/.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-CONTEXT.md

22 KiB
Raw Blame History

Phase 11.2: summercms.io Alpha 0.1 landing page on SummerCMS - Context

Gathered: 2026-09-29 Updated: 2026-10-01 (phase reshaped into the Alpha 0.1 landing page; newsletter moved to 11.3) Status: Ready for planning

How to read this file. Decision numbers are stable because ROADMAP.md cites them. The decisions that apply to 11.2 are D-01 to D-05 (three repos only), D-07, D-09 and D-12 from the 2026-09-29 discussion, plus D-23 to D-45 from 2026-10-01. D-06, D-08, D-10 and D-11 are superseded and kept only as one-line stubs. D-13 to D-22 belong to Phase 11.3 and are kept verbatim in the <phase_11_3_handoff> section at the end. The 11.2 planner ignores that section.

## Phase Boundary

summercms.io replaces its "Under construction" page with the Alpha 0.1 landing page from the claude.ai/design handoff in design/. The page is a Nuxt 4 static site, English only and i18n-ready. It is embedded in one SummerCMS binary that also serves the Phase 11.1 docs at /docs. It deploys to the rome server behind nginx and supervisor, following a documented and scripted procedure.

Three new repos: sm-summercmsio-app, vue-summercmsio-app and sm-summercmsio-plugin. The framework (summercms.go) gets three small, scoped changes: Postgres 15 support verified and documented (D-25), an optional docs-to-site link (D-41) and a v0.1.0 tag (D-42).

Out of scope: newsletter signup, the Polish locale and the subscriber admin. These are Phase 11.3.

## Implementation Decisions

Repos and layout (2026-09-29, still in force)

  • D-01: Three new repos follow the sm- naming convention (oc-/wn- in October/Winter):

    • sm-summercmsio-app is the root app. It builds the binary, holds the other pieces and wires the plugins.
    • vue-summercmsio-app is the Nuxt 4 site, held inside the root app as vue-fonoteka-app is in the fonoteka project.
    • sm-summercmsio-plugin is the site plugin. It holds everything specific to summercms.io: serving the embedded Nuxt build at /, mounting the 11.1 docs output at /docs, and any site-specific data or routes. This is the proven WinterCMS pattern from figs.org.pl (figs/website) and golem15.com.

    Public static-site serving goes in sm-summercmsio-plugin, not in a new framework module. (The fourth repo, sm-newsletter-plugin, moved to 11.3.) — Reversibility: costly — repo names and module paths are referenced by the go.work/replace wiring and every import.

  • D-02: Go module paths live under git.golem15.com/golem15/, like the framework (git.golem15.com/golem15/summercms): for example git.golem15.com/golem15/sm-summercmsio-plugin. — Reversibility: one-way — a published Go module path is baked into every importer.

  • D-03: The root app wires the framework and the site plugin the way fonoteka.go does today (a go.work workspace with a local replace during development).

Website build and hosting (2026-09-29, still in force)

  • D-04: The site is Nuxt 4 with nuxt generate (static output), the same stack as vue-fonoteka-app. Every route is real prerendered HTML, so link previews and search engines see content.
  • D-05: The generated site and the 11.1 docs output are embedded in the Go binary with go:embed. One binary serves / and /docs. The build order (nuxt generate, then summer docs:build, then go build) is documented and scripted in the root app.
  • D-06: Superseded. The site is English only for now (see D-33); Polish moves to 11.3.
  • D-07: The site's served responses must be indexable. The admin-only boardwalk behaviour (noindex, nofollow, admin CSP, base-path rewrite) must not apply to the public site. The site plugin sets its own cache headers: immutable for hashed assets, no-cache for HTML.

Landing page content (2026-09-29)

  • D-08: Superseded by the design handoff's section list: header, hero, Why, Features, From WinterCMS, Get started and footer.
  • D-09: The "From WinterCMS" section carries the seasons lineage: it started in October (OctoberCMS), went through Winter (WinterCMS), and now it's time for Summer. The handoff's copy is final. The theme lives in the brand (sun, "A new dawn", "Something bright is here") and is not added as extra copy.
  • D-10: Superseded. The tone is "Alpha 0.1 is out", per the handoff.
  • D-11: Superseded. Source links are on the page (see D-38).
  • D-12: A "Built by Golem15" credit links to golem15.com. It is an addition to the handoff's footer, styled as a footer link.

Design handoff

  • D-23: The handoff in design/ (README.md, SummerCMS Landing.dc.html) is high fidelity. Colors, type, spacing, copy, breakpoints and the two interactions (scroll-spy and copy-to-clipboard) are matched exactly. The only copy deviations are those decided here: D-12 (credit), D-39 (clone line) and D-26 (the Postgres chip).

Database at runtime

  • D-24: The binary keeps the stock summer serve path, which opens Postgres unconditionally (modules/surf/serve.go:40). There is no framework change to make the DB optional. rome already runs PostgreSQL 15.19 (Debian 12).
  • D-25: Verify the framework on PostgreSQL 15. Run the framework's database test suites (lagoon, conga and the rest that use postgres:16-alpine) against a postgres:15 image. If they pass, change the documented requirement from "PostgreSQL 16" to "PostgreSQL 15 or newer" in the root README.md, docs/setup/installation.md and any other page that states it (the docs checker must stay green). If any test fails on 15, stop and ask the user. Do not quietly work around it or upgrade rome.
  • D-26: The "PostgreSQL 16" chip in the Get started section follows D-25 and reads "PostgreSQL 15+".
  • D-27: The app uses a dedicated Postgres role (summercms) that owns a dedicated database (summercms_io). The password lives in the app's config or env on the server, never in git. DEPLOY.md documents createuser/createdb, and every deploy runs the app's migrate command before restarting.

Server and deploy flow

  • D-28: The binary is built locally for linux/amd64 by a script in sm-summercmsio-app (nuxt generate, then summer docs:build --base-url /docs, then go build), then rsynced to rome with its config. The server needs neither Go nor Node.
  • D-29: /backend and the admin API are not exposed publicly. nginx proxies only / and /docs to the binary and denies the admin paths. Nothing needs administering until 11.3.
  • D-30: TLS uses rome's existing certbot / Let's Encrypt setup. DEPLOY.md shows the nginx server block with the certbot-managed certificate paths, HTTP to HTTPS redirect, gzip and proxy headers.
  • D-31: Cutover replaces the existing summercms.io nginx server block in place. The current "Under construction" block is saved in DEPLOY.md (or alongside it) as the rollback.
  • D-32: The binary runs under supervisor as a dedicated system user on a localhost-only port. The user, port and paths are Claude's discretion and are written down in DEPLOY.md and the supervisor program config.

Design to Nuxt

  • D-33: The site is i18n-ready. @nuxtjs/i18n is installed and configured with the single locale en, and all page copy lives in locales/en.json. 11.3 adds pl.json and a switcher without touching component markup.
  • D-34: Roboto (300/400/500/700) and Roboto Mono (400/500) are self-hosted through @nuxt/fonts, the same module as vue-fonoteka-app. They are downloaded at build time and embedded, so visitors make no request to Google.
  • D-35: Styles are plain CSS. The handoff tokens are CSS custom properties on :root, used by scoped component styles. No Tailwind.
  • D-36: The sun artwork is the original from the live site, http://summercms.io/logo.png (746×744 transparent RGBA PNG, 513 KB). It replaces the handoff's sun-crop.png placeholder. Resized and compressed copies are made for the hero badge (180px), the header logo (30px), the favicon set and the OG image.
  • D-37: SEO uses the full @nuxtjs/seo module, as vue-fonoteka-app does: meta, Open Graph and Twitter tags, OG image, schema.org, sitemap and robots. Whatever it generates must work with nuxt generate and be embedded (no runtime OG rendering in the Go binary).
  • D-45: The prototype's showRays and scrollSpy flags become Nuxt app config, both defaulting to true.

Planning-session decisions (2026-10-01, after research)

  • D-46: D-41 is extended. docs/site.yaml gets an optional site_url and an optional site_label. summer docs:build and summer docs:serve accept --site-url and --site-label flags that override them, the same way --base-url overrides base_url. The framework's own site.yaml sets neither, so its docs output is unchanged. If site_label is unset, the label is the URL's host, or "Home" for a relative URL. The summercms.io build passes --site-url / and --site-label summercms.io, so the header reads "← summercms.io".
  • D-47: The landing page keeps the handoff's extension-less /docs/... hrefs exactly as written (for example /docs/backend/admin-spa). sm-summercmsio-plugin answers an extension-less /docs/x with a 301 to /docs/x.html when that page exists. The SC3 link check follows the redirect and requires the final 200.
  • D-48: (2026-10-01, after plan 11.2-01) The site's repositories are named summercmsio, not summercms, to tell the website apart from the framework on Gitea. The user created the remotes git@git.golem15.com:golem15/vue-summercmsio-app.git, git@git.golem15.com:golem15/sm-summercmsio-app.git and git@git.golem15.com:golem15/sm-summercmsio-plugin.git. Local directories, Go module paths (git.golem15.com/golem15/sm-summercmsio-app, git.golem15.com/golem15/sm-summercmsio-plugin), submodule URLs and the npm package name follow. Unchanged: the framework module git.golem15.com/golem15/summercms, the Go package summercms, the plugin ID golem15.summercms, the plugin directory plugins/golem15/summercms and the binary summercms-io. Each new repository gets origin set to its remote at creation; nothing is pushed during the phase, and DEPLOY.md's step changes from creating the remotes to pushing them. Supersedes the repository names in D-01, D-02 and D-43. — Reversibility: one-way once pushed (module paths).

Get started accuracy

  • D-38: Both Source links point to https://git.golem15.com/golem15/summercms. The repo currently requires login, so making it public is a launch checklist item in DEPLOY.md, done manually by the user before cutover. The link check (roadmap criterion 3) verifies anonymous access at cutover time.
  • D-39: The terminal card gets a clone step so it works when pasted into a fresh shell: a # get the framework comment, then $ git clone https://git.golem15.com/golem15/summercms and $ cd summercms, before the existing go install ./cmd/summer group. The Copy button copies every command line in order (now six), without $ or comments.
  • D-40: A scripted check proves the terminal commands work verbatim. It runs them in a temp clone with a temp GOBIN, ending with ./bin/hello greeter:hello. If that command needs a database or other setup, the check provides it, and the copy is not changed again without asking. The command list lives in one place shared by the page and the check, or the check asserts that the two match, so they cannot drift.

Docs and release

  • D-41: Framework change: an optional site_url key in docs/site.yaml adds a link back to the main site in the docs header (internal/docsite/theme/templates/header.html), for example "← summercms.io". When the key is unset, the output is unchanged. The change also updates the docs page that describes site.yaml, and summer docs:build --check and TestDocsTree stay green. summercms.io sets it to /.
  • D-42: The framework is tagged v0.1.0 (Alpha 0.1). sm-summercmsio-app requires git.golem15.com/golem15/summercms v0.1.0 (with the local replace during development), and the embedded docs are built from that tag, so the page, the docs and the code agree. The user confirms before the tag is created and pushed. — Reversibility: one-way — a pushed Go module version tag is cached by proxies and cannot be reused.
  • D-43: The new repos live as siblings of summercms.go in the meta repo directory (summercms/sm-summercmsio-app/, like fonoteka.go). vue-summercmsio-app and sm-summercmsio-plugin are submodules inside sm-summercmsio-app. Creating the remotes on git.golem15.com is a manual step the plan lists for the user.
  • D-44: Every link on the page is verified against the built docs output (roadmap criterion 3), not assumed. As of 2026-10-01 all ten /docs/... targets in the handoff exist as pages in docs/.

Claude's Discretion

  • The system user, port and filesystem paths on rome (D-32).
  • Whether the Nuxt build and the docs are embedded by sm-summercmsio-plugin or by the root app, and how the plugin mounts /docs.
  • The 404 handling for unknown paths (for example Nuxt's generated 404.html with a 404 status).
  • Image formats and sizes derived from the sun artwork, and the OG image layout within the brand.
  • Where the D-40 command check lives (root app script or test) and how it shares the command list with the page.
  • How prefers-reduced-motion affects smooth scrolling.

<canonical_refs>

Canonical References

Downstream agents MUST read these before planning or implementing.

Phase scope and design

  • .planning/ROADMAP.md § Phase 11.2: goal, repos, naming convention, design source and success criteria.
  • .planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/design/README.md: the high-fidelity handoff (layout, tokens, copy, interactions). Must read.
  • .planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/design/SummerCMS Landing.dc.html: the HTML design reference. Ignore its support.js runtime.
  • http://summercms.io/logo.png: the original sun artwork (D-36).

Docs build (Phase 11.1)

  • .planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-CONTEXT.md: how the docs are built (summer docs:build, static output, internal/docsite).
  • docs/site.yaml: docs config (base_url, and the new site_url from D-41).
  • cmd/summer/docs.go and internal/docsite/load.go: the --base-url flag and how it overrides base_url.
  • internal/docsite/theme/templates/header.html: the docs header that D-41 changes.

Framework touch points

  • modules/surf/serve.go: serve opens the database unconditionally (D-24).
  • README.md, docs/setup/installation.md and docs/plugins/testing.md: where "PostgreSQL 16" is stated (D-25).
  • modules/boardwalk/README.md: the embedding and cache-header reference. Its admin security headers must not be reused (D-07).
  • examples/hello: the app the terminal card builds and runs (D-40).

App wiring and frontend references

  • ../fonoteka.go (main.go, go.work, summer.yaml, config/): the reference for app wiring (D-03).
  • /media/nvme/dev/golem15/fonoteka/vue-fonoteka-app (nuxt.config.ts, package.json): Nuxt 4 with @nuxt/fonts, @nuxtjs/i18n v10 and @nuxtjs/seo, the setup to mirror (D-33, D-34, D-37). Note its comments about i18n on static builds (_i18n/**/*.json).
  • /media/nvme/dev/golem15/figs.org.pl/plugins/figs/website: the WinterCMS site-plugin pattern (D-01).

</canonical_refs>

<code_context>

Existing Code Insights

Reusable Assets

  • boardwalk: its go:embed handling and its hashed-asset versus HTML cache headers are a pattern to copy into sm-summercmsio-plugin. Its noindex, admin CSP and base-path rewrite are not.
  • summer docs:build --base-url /docs: already emits docs with the right link prefix for mounting under /docs.
  • summer build: generates the app's main.go and plugins.gen.go from summer.yaml, as in fonoteka.go.
  • The 11.1 docs checker (TestDocsTree, docs:build --check): it already verifies internal links and commands across docs/, so the D-41 change must keep it green.

Established Patterns

  • Plugins are compiled Go modules registered at build time (no runtime loading).
  • Every application repo is a go.work workspace that requires the framework with a local replace.
  • Framework READMEs and docs never name a consuming application, so the D-41 docs use a neutral example URL, not summercms.io.
  • Unit tests are the last plan of the phase. go vet and go test ./... stay green in every repo.

Integration Points

  • sm-summercmsio-app registers sm-summercmsio-plugin, and the plugin's routes serve / (Nuxt output) and /docs (docs output) from the surf mux.
  • nginx on rome proxies / and /docs to the binary on localhost and denies the admin paths (D-29).
  • Phase 11.2 depends on 11.1 being executed, because the docs build output feeds the embed step.

</code_context>

## Specific Ideas
  • The page must look exactly like the handoff. "Final" means final, apart from the decided deviations (D-12, D-26, D-39).
  • One visual identity across summercms.io, /docs and the admin: Direction C v2, dark navy and sunny yellow, with the sun.
  • The terminal card must work when pasted into a fresh shell (D-39, D-40).
## Deferred Ideas
  • Composing and sending newsletters, templates and the campaign CLI, ported from the PHP plugin in a later phase. That phase also merges confirmed subscribers with the user-based audience.
  • CSV export of subscribers. It waits for a framework export or toolbar capability.
  • Renaming fonoteka.go to sm-fonoteka-app is a separate task.
  • Translating the docs into Polish.
  • go install git.golem15.com/golem15/summercms/cmd/summer@latest as the install command. This needs the module path to be go-gettable (a public repo plus go-import meta).
  • A CI pipeline that builds and publishes the binary, instead of local build and rsync.
  • Making the database optional in summer serve, for apps without data.

Reviewed Todos (not folded)

  • "Nest framework packages under modules/" is already delivered by Phase 10.2.
  • "Backend admin personal API tokens" is unrelated to the public site.
  • "Extend fetchguard into a guarded outbound http.Client" is unrelated.

<phase_11_3_handoff>

Carried to Phase 11.3: newsletter decisions (2026-09-29, verbatim, ignore when planning 11.2)

ROADMAP.md § Phase 11.3 points here. When 11.3 is discussed, these move into 11.3-CONTEXT.md.

  • Repo: sm-newsletter-plugin is the reusable signup plugin, with Go package newsletter, module path git.golem15.com/golem15/sm-newsletter-plugin.
  • D-06 (moved): The site gains Polish (for example / and /pl/, via @nuxtjs/i18n as in vue-fonoteka-app). The confirmation and welcome mails are localized to the subscriber's locale through phrasebook/postcard. The 11.1 docs stay English-only.

Signup and double opt-in

  • D-13: Bot protection is a honeypot field, a minimum time-to-submit check and a per-IP surf rate-limit bucket. There is no captcha or third-party script.
  • D-14: A required consent checkbox ("I agree to receive SummerCMS updates") links to a privacy note. The plugin stores the consent timestamp, IP and locale with the subscriber as GDPR evidence.
  • D-15: Every signup gets the same neutral "check your inbox" response, and the response never reveals list membership:
    • pending: resend the confirmation, throttled
    • unsubscribed: new confirmation cycle
    • confirmed: no mail
  • D-16: The confirm link is tokenized. On success it redirects to a localized thank-you page on the site and sends a welcome mail with the unsubscribe link, as figs.org.pl does. Invalid or expired tokens redirect to a localized error page.
  • D-17: Unsubscribe is a tokenized link in every mail. It marks the subscriber unsubscribed and redirects to a localized "unsubscribed" page.

Plugin shape

  • D-18: Subscribers are standalone: the plugin has its own subscribers table and no dependency on the user plugin. Merging confirmed subscribers with a user-based audience (as the PHP AudienceResolver does) belongs to the later sending phase.
  • D-19: The stub ships only the subscribers table, but with the fields sending will need: email, status (pending, confirmed, unsubscribed), locale, confirmation and unsubscribe tokens, confirmed_at, unsubscribed_at, source (form or admin), consent timestamp and IP, and timestamps. The newsletter, template and recipient tables are ported from PHP in the sending phase. — Reversibility: costly — the migration shape becomes the base that the sending phase extends.
  • D-20: The admin backend (cabana YAML list and form) supports list, search, filters (status, locale), view, add, edit and delete. Delete is the GDPR erasure path.
  • D-21: An admin-added subscriber is created as pending with source=admin, and the normal confirmation mail is sent. Nobody lands on the list without their own confirmation.
  • D-22: Composing and sending newsletters, templates and campaign CLI are out of scope.

11.3 discretion (from 2026-09-29)

  • How long confirmation tokens stay valid and the resend throttle interval; token format (random opaque vs signed); exact URL scheme for the API endpoints and localized result pages.

11.3 references

  • /media/nvme/dev/golem15/horoskopia.eu/plugins/golem15/newsletter: Golem15.Newsletter PHP (github.com/golem15com/wn-newsletter-plugin). Port source; do not modify.
  • /media/nvme/dev/golem15/figs.org.pl/plugins/figs/website: working subscribe flow (models/Subscriber.php, models/subscriber/{fields,columns}.yaml, components/subscribe/, hash confirm/resign routes in routes.php).
  • modules/postcard, modules/phrasebook, modules/surf, modules/lagoon, modules/cabana, modules/pact READMEs.
  • 11.2 decision D-29 means /backend is not public yet. 11.3 must decide how the subscriber admin is reached (for example an IP allowlist or basic auth in nginx).

</phase_11_3_handoff>


Phase: 11.2-ready-to-share-summercms-io-website-and-newsletter-plugin Context gathered: 2026-09-29, updated 2026-10-01