Skip to content

docs(design-proposals): add the platform and catalogs split proposal - #106

Open
myasnikovdaniil wants to merge 4 commits into
mainfrom
proposal/cozystack-split
Open

myasnikovdaniil wants to merge 4 commits into
mainfrom
proposal/cozystack-split

Conversation

@myasnikovdaniil

Copy link
Copy Markdown
Contributor

This PR adds design proposal for splitting cozystack into platform and two catalogs, virtualization and services. It supersedes community#43 and community#46, what is taken from each is listed in the Scope section, both can be closed after this one is accepted.

Main part for review is contracts K1-K6 between platform and catalogs, they are the seam of the split and the rest depends on them. Contracts have no versions of their own, catalog declares minimum platform version and the properties it needs from network, storage and GPU.

Proposal also sets contribution policy for new applications: they go to separate catalog repositories and connect through PackageSource and cozypkg (marketplace from 1.7). Step 0 work starts before acceptance and will be tracked in an epic in cozystack/cozystack.

Before review

  • If this revises a merged design proposal: it adds a decision record under that proposal's decisions/ directory, or says below why none is needed.

New proposal, it does not revise a merged one.

DCO

  • Commits are signed off (git commit --signoff).

Split Cozystack into the platform and two catalogs, virtualization and
services, with contracts K1-K6 between them, a contribution policy for
new applications and a rollout in four steps. Supersedes community#43
and community#46.

Assisted-By: LLM
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b42bf4d6-a942-453d-afec-235f8526235e
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Assisted-By: LLM
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
@IvanHunters

Copy link
Copy Markdown
Contributor

Strong proposal. Monorepo-first with separate repositories as the last step is the right call, the contracts are written out concretely instead of hand-waved, and the cut is backed by data. The boundary lint with a measurable zero-forbidden-edges criterion is the right spine to hang it on. Most of what follows is about sequencing against work already in flight, plus a few failure modes the contracts don't cover yet. I checked the current-state claims against main; where I cite code, that's where it's from.

Blocking (would want these resolved before it leaves Draft)

  1. K2 transport has no failure story for the projection controller. Swapping the cozystack-values Secret for a typed resource plus one controller that projects _namespace into a ConfigMap per tenant namespace puts a new component on the critical path of every chart in every tenant. The three writers today are real and have already drifted, so folding them into one is worth doing: packages/core/platform/templates/apps.yaml for cozy-system, packages/system/cozystack-basics/templates/cozystack-values-secret.yaml for tenant-root, packages/apps/tenant/templates/namespace.yaml for children. But only the happy path is covered. What does a new tenant get when the controller is down or lagging? How is a partial projection, half the namespaces updated, detected? And what guarantees the ConfigMap is populated before the first HelmRelease that valuesFrom-references it? There's also a trap in the migration: "missing key means empty string" plus the transport swap means a present-but-empty key renders a chart cleanly with an empty ingress or monitoring host, which is exactly the silent failure the contracts section is trying to kill. The dual-write window ("both objects exist, releases switch in one reconcile") needs the switch trigger, the source of truth while both exist, and the rollback of a half-switched state all spelled out.

  2. The backup driver handoff is a cross-component ownership race that the "mechanism lands, apps switch, old code removed" ordering doesn't address. On main the dispatch already keys off strategyRef.Kind and the default branch is a silent skip (return ctrl.Result{}, nil), so moving a driver out means the platform drops its case and the catalog adds a controller for the same kind. With platform and catalog on independent versions inside the N-1/N/N+1 window, two bad states open up: the kind orphaned (platform skips it, catalog not upgraded yet), which silently stops taking backups, or double-owned, where both reconcile the same BackupJob. Step 1 says an unknown kind should end the job with "driver not installed", but flipping today's silent skip to a hard fail reds every handed-off BackupJob during the upgrade window. So the design has to tell "kind handed to a catalog, mid-transition" apart from "kind never known". internal/backupcontroller/restorejob_controller.go is the sharper edge: it still imports velerov1 and runs best-effort Velero cleanup for any unrecognised kind on delete, so once Velero moves to virtualization the platform restorejob is reaping a catalog's restore side-state.

  3. Policy enforcement is tied to acceptance instead of to the marketplace actually being safe. The policy shuts the monorepo path for new apps with no grace period, but the one sanctioned path isn't safe yet. createOrUpdateHelmRelease in internal/operator/package_reconciler.go overwrites any HelmRelease of the same namespace and name, ownerReferences included, with no owning-Package guard. Connect still installs a publisher chart as cluster-admin. And internal/marketplace/naming/naming.go collapses "." to "-", so acme.web and acme-web collide. Those are readiness items 2/3/5, and they should gate enforcement. Otherwise there's a window where the monorepo door is shut and the door left open has a cluster-admin takeover behind it.

Non-blocking but worth addressing

  1. The Go backup SDK is effectively a seventh contract and nothing in K1-K6 covers it. Once drivers live in separate modules they import pkg/backups/ (credential projection, the BackupClass resolver, Job and status helpers) and the strategy.backups.cozystack.io types: a public Go ABI with no schema, no deprecation window, and no place in the compatibility rule. K5 versions cozy-lib on the Helm side but not this. Either make it a contract, or state that third-party catalogs are limited to Job-template drivers.

  2. The Context section on backups is stale against main. It describes 11 strategies as methods of shared reconcilers with a switch on kind, plus operator-type imports, but internal/backupcontroller/backupjob_controller.go already dispatches on strategyRef.Kind from a single API group and imports no operator types. Decoupling Backup-kind from application-kind is done; what's left is the physical move of the driver methods, not the switch. Fixing this changes how a reviewer sizes the remaining work.

  3. Lookup removal is treated as an adjacent workstream rather than a prerequisite. There are ~168 lookups in chart templates, and the removal direction is both active and accepted: the user-secrets-api proposal (merged) with its postgres wave (#4922) replaces the password-preservation lookups, design-proposal: tenant-supplied secrets by reference through External Secrets Operator #82 covers ESO, #1942 the broader replacement. The boundary lint counts lookup edges but doesn't remove them, and the proposal doesn't say which lookups cross the boundary or who removes them. K3 network.stableVMAddress is the lookup "kubeovn.io/v1" "IP" in vm-instance, already guarded after it silently skipped (#4013, #4025, #5061); turning it into a declared property while the code still does a raw lookup is half a fix. Worth saying whether removal gates step 1/2, and what each lookup-removing contract puts in its place.

  4. This overlaps with the Cozystack API v1 proposal (design-proposal: Cozystack API v1 #73), and the two don't reference each other. design-proposal: Cozystack API v1 #73 closes the open spec object and makes server-side field removal mean something, against the same hardcoded dead-field list in pkg/registry/apps/application/rest.go that K1 moves toward admission policy. If design-proposal: Cozystack API v1 #73 closes spec to v1 and validates server-side, the plan to move warnings into a per-chart ValidatingAdmissionPolicy needs to be reconciled with it.

  5. The seven cross-version checks are inferred from code and haven't run on a cluster, yet they carry the weight of the contracts: the silent value drop for K4, the annotation-stops-the-API case for K1 (that one I can confirm is real in pkg/cmd/server/start.go). Running at least checks 1, 2 and 4 before K1/K2/K4 are finalised would put the contracts on observed behaviour instead of assumption.

Smaller

  1. "Minimal installation is the platform without catalogs" still ships SeaweedFS, COSI, Keycloak, cert-manager and monitoring. Object storage sits in the platform unconditionally while the backup framework is optional. Is object storage meant to be always-on with no catalogs and no backups, or optional too?

  2. One contract version for all six couples them: a catalog that only depends on K1 and K2 still has to bump its minimum and re-test when K6 changes. The trade-off is defensible, but the cost is worth naming out loud.

  3. The core-split trigger, "when platform releases start holding core fixes", is subjective next to the measurable step-3 criteria.

Questions

  • K2: what triggers a release switching from the Secret to the ConfigMap, and what's the source of truth while both exist? How do you roll back a half-switched state?
  • K2: HA and degradation behaviour of the projection controller?
  • Backups: who owns a strategy kind during the N-1/N/N+1 handoff window, and how is "handed off, not yet installed" told apart from "unknown"?
  • Is the Go backup SDK a contract, or are third-party catalogs limited to Job-template drivers?
  • Which lookups cross the platform/catalog boundary, who removes them, and is removal a hard prerequisite of step 1/2?
  • Does policy enforcement gate on readiness items 2-7 landing, or on acceptance?
  • What pins the N+1 contract schema so catalog CI against it stays deterministic?
  • Is object storage always-on, or optional without catalogs and backups?
  • How does this reconcile with API v1 (design-proposal: Cozystack API v1 #73)?

…ve them to authors

A separate organization for community applications would have no
access to the project's e2e runners and no CNCF rules, while still
carrying the project's name. An application either meets the bar for
one of our catalogs and lives in this tree until the catalogs move, or
its author publishes it as their own catalog. An interim repository
would only have to be moved again once the catalogs get their own.

Assisted-By: LLM
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Where a new application goes is decided together with whether the
project takes it, and that is a proposal. An e2e requirement would
mostly re-test the upstream operator, and a fixed number of users is
easy to game.

Assisted-By: LLM
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants