Initial commit: SummerCMS Go planning docs and research
Records the move from Scala to Go, the compiled-plugin decision, the Płytarium port as the v1 target, and the Go ecosystem research that backs the choice. No code yet.
This commit is contained in:
73
.planning/notes/v1-target-plytarium.md
Normal file
73
.planning/notes/v1-target-plytarium.md
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: v1 target — Płytarium port
|
||||
date: 2026-09-16
|
||||
context: Chosen during the opening exploration session over the smaller `inventory` project. Defines v1 scope and the acceptance rule.
|
||||
---
|
||||
|
||||
# v1 target: Płytarium port
|
||||
|
||||
**Definition of done for v1:** `vue-fonoteka-app` (Nuxt 4) runs unchanged against the SummerCMS Go backend, and `fonoteka-mcp` talks to it through the same OAuth2 flow it uses today.
|
||||
|
||||
Płytarium is the `fonoteka` project at `/media/nvme/dev/golem15/fonoteka`. It is a headless WinterCMS backend for a household record collection, forked from `wn-inventory-plugin`, on the Golem15 starter v1.1.8 LTS.
|
||||
|
||||
## Why this one
|
||||
|
||||
- Headless. No theme engine needed in v1.
|
||||
- Actively developed and actually used, so "done" is testable by daily use.
|
||||
- It forces every ecosystem claim at once: OAuth2/OIDC provider, background jobs, realtime, search, file uploads, external APIs, org-scoped permissions.
|
||||
- Roughly three times the size of `inventory`, which was the alternative. Large enough to be real, small enough to finish.
|
||||
|
||||
## What must be ported
|
||||
|
||||
### Domain plugin `golem15.fonoteka`
|
||||
|
||||
| Aspect | Count |
|
||||
|---|---|
|
||||
| Models | 25 (Album, Artist, Collection, Genre, Style, AlbumRating, AlbumReservation, ApiToken, CollectionEditor, CollectionInvitation, CsvImport, CsvImportRow, Notification, OAuthAuthCode, OAuthClient, OAuthRefreshToken, OrgAiCredential, OrgDiscogsCredential, PendingInvitationRegistration, Settings, UserAiCredential, UserCollectionContext, UserDiscogsCredential, WishlistDigestQueue, WishlistSubscription) |
|
||||
| Admin controllers | 5 (Albums, Artists, Collections, Genres, Styles) with YAML-driven forms and lists |
|
||||
| API routes | 160 (collections, albums, wishlist, sharing, notifications, realtime channels, CSV export, locale, context) |
|
||||
| Migrations | 27 |
|
||||
| Console commands | 3 (IssueOAuthClient, PruneNotifications, ReindexAlbums) |
|
||||
| Jobs | CSV import, wishlist digest |
|
||||
| Test files | 143 |
|
||||
| PHP lines | ~57k |
|
||||
|
||||
### Stack plugins in play
|
||||
|
||||
| Plugin | Role in Płytarium |
|
||||
|---|---|
|
||||
| user | Accounts, JWT, organizations, the base of the OAuth2 provider |
|
||||
| backend | Admin panel improvements |
|
||||
| apparatus | DI, scenarios, backend tooling |
|
||||
| golem | AI integration (cover recognition via Anthropic / OpenAI) |
|
||||
| translate | pl / en |
|
||||
| websockets | Realtime via Centrifugo |
|
||||
| userfriends | Friends and groups for collection sharing (optional) |
|
||||
| journal | Blog / notes (optional) |
|
||||
| feedback | Feedback submissions |
|
||||
| sitemap | Sitemap |
|
||||
|
||||
### Hard parts, and the Go answer from research
|
||||
|
||||
| Hard part | Go pick |
|
||||
|---|---|
|
||||
| OAuth2 provider used by ChatGPT connector and MCP | `zitadel/oidc` |
|
||||
| JWT for the SPA | `golang-jwt/jwt` |
|
||||
| Queued jobs (CSV import, digests) | `riverqueue/river` (Postgres-backed) |
|
||||
| Realtime notifications | `coder/websocket`, or keep Centrifugo and port only the publisher |
|
||||
| Search (Typesense via Scout today) | Typesense Go client |
|
||||
| File uploads, cover images | `gocloud.dev/blob` |
|
||||
| Discogs, Anthropic, OpenAI HTTP clients | stdlib `net/http`, official Anthropic Go SDK |
|
||||
| Validation of YAML `rules:` | `go-playground/validator` |
|
||||
|
||||
## Out of scope for v1
|
||||
|
||||
- Server-rendered themes and components. Second target: keios.eu (see `../seeds/keios-port-themes-payments.md`).
|
||||
- Payments (paymentgateway, pgstripe). Same second target.
|
||||
- Chat, forum, video. wavepath.org territory, later.
|
||||
- WASM extension API. See `../seeds/wasm-extension-api.md`.
|
||||
|
||||
## Reference material
|
||||
|
||||
- `wavepath.org/plugins/golem15/oauthserver` (11k lines, has tests and `INTEGRATING.md`) is the fuller OAuth2 implementation in the stack. Use it as the behavioral reference for the user plugin's OIDC port even though Płytarium's own OAuth models are smaller.
|
||||
- `fonoteka/.planning/` holds the GSD history of the PHP project, including `STARTER-LINEAGE.md`.
|
||||
45
.planning/notes/why-go-not-scala.md
Normal file
45
.planning/notes/why-go-not-scala.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: Why Go, not Scala
|
||||
date: 2026-09-16
|
||||
context: Exploration session that opened the summercms.go repository. Records the language decision so it is not relitigated.
|
||||
---
|
||||
|
||||
# Why Go, not Scala
|
||||
|
||||
## Background
|
||||
|
||||
SummerCMS started in February 2026 as a Scala 3 rewrite of WinterCMS. The stack was direct-style Scala on JDK 21 virtual threads (Tapir, Ox, Magnum, Jig). Three infrastructure modules shipped as independent sbt projects: `summer-compass` (config), `summer-phrasebook` (i18n), `summer-bonfire` (console). They live in the meta repo under `modules/`.
|
||||
|
||||
The effort stalled before any application ran. It was building Illuminate-shaped infrastructure bottom-up with no real app pulling requirements.
|
||||
|
||||
## The deciding test
|
||||
|
||||
We gave both ecosystems the same task: an OAuth2 server with full OIDC support.
|
||||
|
||||
- **PHP / WinterCMS.** The agent took `league/oauth2-server` as a model, read the existing Golem15 user plugin forks, reused the JWT primitives already in the stack, and produced a plugin. The Płytarium project today runs its own OAuth2 provider on this basis.
|
||||
- **Scala.** The agent searched and found nothing reusable. It proposed a from-scratch implementation. The proposal was rejected as too large to fund.
|
||||
|
||||
The Scala language was never the problem. The ecosystem was. A CMS is mostly glue over solved problems, and glue needs the problems to be solved already.
|
||||
|
||||
## Why Go passes the same test
|
||||
|
||||
The Go ecosystem research (see `../research/go-ecosystem.md`) found maintained, widely used libraries for every concern in the Illuminate module map, including the one that failed in Scala: `zitadel/oidc` is an OpenID-certified provider and client library. Go is closer to PHP's situation than Scala is: many small, boring, well-tested libraries, and several Laravel-shaped frameworks and CMSes to read for patterns (Goravel, PocketBase, GoFrame).
|
||||
|
||||
## What Go changes
|
||||
|
||||
Go is statically compiled, so "drop a plugin folder and it autoloads" does not exist. The decision:
|
||||
|
||||
- **Primary: compiled plugins.** Caddy/xcaddy model. `plugins/` is a workspace of Go modules. A generated import list registers them. `summer make:plugin` scaffolds, `summer build` rebuilds, and a watch-rebuild loop in dev makes it feel like WinterCMS. Full type safety, plugins extend each other through ordinary Go interfaces, `go test` works.
|
||||
- **Later: WASM extension API.** A narrow, sandboxed surface for untrusted third-party extensions. Only after the core plugin API is stable. See `../seeds/wasm-extension-api.md`.
|
||||
- **Rejected:** stdlib `plugin` (Linux-only, toolchain lockstep, no unload), yaegi (lags Go releases, partial generics), RPC plugins (interface too wide to marshal for model extension).
|
||||
|
||||
## What carries over from the Scala work
|
||||
|
||||
- The module naming scheme in `IDEA_LIB_NAMES.md` (backpack, compass, phrasebook, bonfire, ...) can be reused as Go package names.
|
||||
- The design of compass (config layering and plugin namespaces), phrasebook (CLDR plurals, namespaced keys), and bonfire (command registry, rich output) is language-neutral and worth porting.
|
||||
- The open ORM question from `STACK.md` (immutable models vs Eloquent mutation) largely disappears in Go: structs are mutable, and both `ent` and `GORM` support Eloquent-like flows.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Not chasing JVM performance. Go's baseline is enough and deploys as one binary.
|
||||
- Not porting Illuminate module by module. The v1 target app pulls what is needed. See `v1-target-plytarium.md`.
|
||||
Reference in New Issue
Block a user