Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc855ec8bb | ||
|
|
0a9b08e1fd | ||
|
|
d6ff08f5d9 | ||
|
|
b1a5c5abc3 |
+12
-9
@@ -1,7 +1,8 @@
|
||||
# Handover — where things stand and how to carry on
|
||||
|
||||
Last updated: 2026-11-04, after **2.1.1** (deployed), Hub **1.7.0** and **contracts 1.0.0**;
|
||||
ROADMAP-v3 updated for the monorepo move and the `crosscode` deploy script (decisions 21–24). **The plan from here is
|
||||
Last updated: 2026-11-05, after **2.1.1** (deployed), **Hub 2.0.0** (released, not deployed yet),
|
||||
**contracts 1.1.0** and the crosscode-site address prices (not deployed yet); ROADMAP-v3 updated for
|
||||
the monorepo move and the `crosscode` deploy script (decisions 21–24). **The plan from here is
|
||||
[ROADMAP-v3.md](./ROADMAP-v3.md)** (Accounts, Church Website, Church App, Cross Code Hub; decisions
|
||||
table near the end). How Cross Events got to 2.x is in [ROADMAP-v2.md](./ROADMAP-v2.md); the round
|
||||
before that in [FEATURES-ROUND-1.md](./FEATURES-ROUND-1.md).
|
||||
@@ -95,13 +96,15 @@ encrypted settings unreadable or break existing installs.
|
||||
crosscode.co.za, with prices) is live. Build order (§9):
|
||||
- **0 — contracts 1.0.0: released 2026-11-04** (`../crosscode-platform`, tag `contracts@1.0.0`):
|
||||
`crosscode.event/1`, `crosscode.church/1`, `crosscode.identity/1`, Hub protocol v2, generated
|
||||
JSON Schema in `packages/contracts/schemas/`. Not published to Gitea's package registry, and
|
||||
won't be (decision 22).
|
||||
- **1 — Hub 2.0.0** (`../events-hub`): Cross Code Hub — products, one licence per product,
|
||||
**one install of each product per customer**, domain per customer, bundles, sibling discovery,
|
||||
**releases per product** (Events from `crosscode-platform` `events@…` tags, keeping the
|
||||
`hope-events` `v…` releases) **with requirements** (`— requires accounts 1.2.0`, Request update
|
||||
disabled until met). Contracts as `vendor/crosscode-contracts-1.0.0.tgz` (ROADMAP-v3 §2, §5).
|
||||
JSON Schema in `packages/contracts/schemas/`. **1.1.0** (2026-11-04) adds `requires` on Hub
|
||||
releases. Not published to Gitea's package registry, and won't be (decision 22).
|
||||
- **1 — Hub 2.0.0: released 2026-11-05** (`../events-hub`, tag `v2.0.0`; plan and what each step
|
||||
did in its `docs/HUB-2.0-PLAN.md` and CHANGELOG): products and one licence per product, protocol
|
||||
v2 next to v1 (Linked products), releases per product with requirements, billing per church
|
||||
(bundles, founding, Website storage, App tiers, prorating), the church's address, AI per church,
|
||||
dashboard per product. **Deploying it:** `events-hub/deployment.md` → "Upgrading to 2.0" (run
|
||||
`check-hub2` first). crosscode-site has the address prices (merged, deploy it too). Then
|
||||
installs show up per product; Cross Events 2.1.x keeps speaking protocol 1 until 2.4.0.
|
||||
- **1b — Cross Events 2.2.0: moves into the monorepo** as `apps/events` with its history, and the
|
||||
**`crosscode` script** (`deploy <church> [events] [website] [app]` — Accounts always —,
|
||||
`status`, `install`, `adopt`) replaces `scripts/deploy.sh` / `install.sh`. Run
|
||||
|
||||
+34
-16
@@ -482,18 +482,23 @@ picks the tags up per product (§5).
|
||||
per product, each at that product's own tag:
|
||||
|
||||
```
|
||||
/opt/crosscode/
|
||||
tools/ the crosscode script (a checkout of main, updated before every run)
|
||||
churches/hopefamily/
|
||||
/var/www/sites/
|
||||
hopefamily/
|
||||
church.env slug, products, domains, ports, PM2 names, database names, folders
|
||||
accounts/ crosscode-platform at accounts@1.0.0
|
||||
events/ crosscode-platform at events@3.1.0
|
||||
website/ crosscode-platform at website@1.2.0
|
||||
gracechurch/
|
||||
…
|
||||
/opt/crosscode/tools/ the crosscode script (a checkout of main, updated before every run)
|
||||
```
|
||||
|
||||
A product's folder can live elsewhere (`church.env` says where), so existing installs such as
|
||||
`/opt/hope-events` don't have to move, and their PM2 names, nginx names and database names stay
|
||||
as they are. Each checkout installs and builds only its own app (`npm ci -w apps/website`, its
|
||||
Every church's installs live under **`/var/www/sites/<church>/`** (decided 2026-11-04; the
|
||||
existing Events installs are in `/var/www` today and move there). The script finds churches by
|
||||
their `church.env`, so `--every-church` is every folder in `/var/www/sites` that has one. A
|
||||
product's folder can be somewhere else if `church.env` says so, but the standard layout is the
|
||||
default for `install` and `adopt`. PM2 names, nginx names and database names keep what an install
|
||||
already has. Each checkout installs and builds only its own app (`npm ci -w apps/website`, its
|
||||
own migrations), so a Website install never builds Events.
|
||||
|
||||
**The script** (run as root or the app user; `crosscode help` lists everything):
|
||||
@@ -508,7 +513,7 @@ own migrations), so a Website install never builds Events.
|
||||
| `crosscode status [<church>]` | Each product's version, latest release, health (from `/health`) and last backup |
|
||||
| `crosscode install <church> --products events,website --domain … ` | A new church (from today's `install.sh`): Accounts always, plus the products named; databases, `.env` files, PM2, nginx and certificates; prints the DNS records needed |
|
||||
| `crosscode add <church> app` | Adds a product to an existing church |
|
||||
| `crosscode adopt <church> --events-dir /opt/hope-events` | Once per existing Cross Events install: moves it onto the monorepo (below) |
|
||||
| `crosscode adopt <church> --events-dir /var/www/<old folder>` | Once per existing Cross Events install: moves it onto the monorepo and into `/var/www/sites/<church>/events` (below) |
|
||||
|
||||
Before Accounts exists (until phase 3), "always Accounts" is skipped for churches that don't have
|
||||
it yet.
|
||||
@@ -528,15 +533,28 @@ it yet.
|
||||
reached.
|
||||
|
||||
**Moving existing Cross Events installs (Events 2.2.0, `crosscode adopt`).** Once per server
|
||||
install, run by Joshua. It writes `church.env` (pointing at the existing folder), changes the
|
||||
checkout's remote to `crosscode-platform`, moves the install's own files to the new paths
|
||||
(`backend/.env` → `apps/events/backend/.env`, likewise the frontend `.env`,
|
||||
`backend/public/uploads/`, `backend/backups/`, `backend/data/*.json`, and `ecosystem.config.js`
|
||||
with its `cwd` paths updated), checks out `events@2.2.0`, builds and reloads. The folder, PM2
|
||||
names, nginx config (it only proxies to ports), database and its name stay the same. If anything
|
||||
fails it moves the files back and returns to the `hope-events` remote and the version it was on.
|
||||
install, run by Joshua. It:
|
||||
|
||||
1. Moves the install's folder from `/var/www/<old folder>` to `/var/www/sites/<church>/events`
|
||||
(skipped if it's already there, e.g. moved by hand) and writes `church.env`.
|
||||
2. Changes the checkout's remote to `crosscode-platform` and moves the install's own files to
|
||||
the new paths: `backend/.env` → `apps/events/backend/.env`, likewise the frontend `.env`,
|
||||
`backend/public/uploads/`, `backend/backups/`, `backend/data/*.json`, and
|
||||
`ecosystem.config.js` with its `cwd` paths updated.
|
||||
3. Checks out `events@2.2.0`, builds, and **re-registers the PM2 processes** under their existing
|
||||
names from the new folder (`pm2 delete` + `pm2 start` + `pm2 save`: PM2 remembers the folder a
|
||||
process was started from, so a reload isn't enough after a move).
|
||||
|
||||
The PM2 names, nginx config (it only proxies to ports, so it doesn't change when the folder
|
||||
moves), database and its name stay the same. If anything fails it moves everything back, returns
|
||||
to the `hope-events` remote and the version it was on, and starts PM2 from the old folder again.
|
||||
Installs that haven't moved yet keep working with `deploy.sh` on the archived repo.
|
||||
|
||||
**Moving a folder by hand before 2.2.0** works too: move it into `/var/www/sites/<church>/events`,
|
||||
then `pm2 delete` its two processes, `pm2 start ecosystem.config.js` from the new folder and
|
||||
`pm2 save`. `deploy.sh` works from wherever the install is, and `adopt` later sees it's already in
|
||||
place.
|
||||
|
||||
---
|
||||
|
||||
## 9. Build order
|
||||
@@ -544,7 +562,7 @@ Installs that haven't moved yet keep working with `deploy.sh` on the archived re
|
||||
| Phase | Version | What | Why this order |
|
||||
|-------|---------|------|----------------|
|
||||
| 0 | contracts 1.0.0 | `crosscode.event/1`, `crosscode.church/1`, `crosscode.identity/1`, Hub protocol v2: schemas, generated JSON Schema, docs, sample payloads, the monorepo set up. **Released 2026-11-04** | Everything else depends on them |
|
||||
| 1 | **Hub 2.0.0** | Cross Code Hub: products, licence per product, per-customer billing with bundles, protocol v2 (v1 still accepted), sibling discovery, AI usage per product, **releases per product with requirements** (§5), contracts as a committed file (§2) | Must exist before a second product is sold, and before Events releases come from the monorepo |
|
||||
| 1 | **Hub 2.0.0** | Cross Code Hub: products, licence per product, per-customer billing with bundles, protocol v2 (v1 still accepted), sibling discovery, AI usage per product, **releases per product with requirements** (§5), contracts as a committed file (§2). **Released 2026-11-05** (crosscode-site address prices merged) | Must exist before a second product is sold, and before Events releases come from the monorepo |
|
||||
| 1b | **Cross Events 2.2.0** | **Moves into the monorepo** as `apps/events`, with its git history (merged in, not copied); its packages become workspaces; the **`crosscode` script** (`deploy`, `status`, `install`, `adopt`; §8) replaces `scripts/deploy.sh` and `scripts/install.sh`; every install moved with `crosscode adopt`; `hope-events` archived. No feature changes, so a problem is clearly the move | Before more products, so every product is released and deployed the same way from the start |
|
||||
| 1c | **Cross Events 2.3.0** | **Authenticator app 2FA** (TOTP) next to email and WhatsApp, with recovery codes; admins can see which method each staff account uses | Independent and useful now; the Accounts migration later carries it over |
|
||||
| 2 | **Cross Events 2.4.0** | Reports `product: "events"` (protocol v2), **Linked products**, the event feed (publish + show events from siblings), `packages/seo` (§11) with canonical links for imported events | Small; ready for when the Website exists |
|
||||
@@ -748,7 +766,7 @@ ranking.
|
||||
|---|----------|----------|
|
||||
| 21 | Repos | **Cross Events moves into `crosscode-platform`** (`apps/events`, Events 2.2.0, history kept); `hope-events` is archived. The Hub stays in its own repo (own server, never installed for a church) |
|
||||
| 22 | Contracts for the Hub | A packed release **committed to the Hub's repo** (`file:vendor/…`), not Gitea's package registry, so no deploy depends on a registry or a token (§2) |
|
||||
| 23 | Deploying | **One `crosscode` script** for every product: `crosscode deploy <church> [events] [website] [app]` always updates Accounts and then the products named, in a fixed order, each rolled back on its own if it fails. Still git checkouts of tags, still run by Joshua; the Hub never deploys (§8) |
|
||||
| 23 | Deploying | **One `crosscode` script** for every product, with every church under `/var/www/sites/<church>/<product>`: `crosscode deploy <church> [events] [website] [app]` always updates Accounts and then the products named, in a fixed order, each rolled back on its own if it fails. Still git checkouts of tags, still run by Joshua; the Hub never deploys (§8) |
|
||||
| 24 | Product requirements | A release can require a minimum version of a sibling (`— requires accounts 1.2.0` in `WHATS-NEW.md`). The Hub **disables Request update** until it's met, and the script refuses to deploy without it (§5, §8) |
|
||||
|
||||
## Still open
|
||||
|
||||
+42
-5
@@ -1,9 +1,13 @@
|
||||
# Hub protocol (version 1)
|
||||
# Hub protocol (versions 1 and 2)
|
||||
|
||||
The contract between a **Cross Events install** (the church's site) and the **Cross Events
|
||||
Hub** (`events-hub.crosscode.co.za`). A copy of this file lives in both repositories
|
||||
(`joshua/hope-events` → `docs/hub-protocol.md`, `joshua/events-hub` → `docs/hub-protocol.md`);
|
||||
change both together.
|
||||
The contract between an **install** (one of a church's products: Cross Events, Accounts, Church
|
||||
Website, Church App) and **Cross Code Hub** (`hub.crosscode.co.za`; `events-hub.crosscode.co.za`
|
||||
keeps working as an alias). Sections 1–5 describe **version 1**, which Cross Events 2.1 speaks;
|
||||
**[version 2](#6-protocol-version-2-hub-20)** (every product, Hub 2.0) is section 6. A copy of this
|
||||
file lives in both repositories (`joshua/hope-events` → `docs/hub-protocol.md`,
|
||||
`joshua/events-hub` → `docs/hub-protocol.md`); change both together. From Hub 2.0 the formats are
|
||||
defined once, in `@crosscode/contracts` (`crosscode-platform/packages/contracts`, `src/hub.ts`,
|
||||
with JSON Schema and samples).
|
||||
|
||||
Principles:
|
||||
|
||||
@@ -288,3 +292,36 @@ grace starts). Blocking the Hub can't keep a suspended licence active for more t
|
||||
fields to a heartbeat or a token doesn't need a new version — both sides ignore fields they
|
||||
don't know.
|
||||
- The Hub accepts protocol `N` and `N-1`; anything else is `400 UNSUPPORTED_PROTOCOL`.
|
||||
|
||||
## 6. Protocol version 2 (Hub 2.0)
|
||||
|
||||
The formats are in `@crosscode/contracts` (`hub.registerRequestSchema`, `hub.heartbeatRequestSchema`,
|
||||
… with JSON Schema in `schemas/hub-*.json` and samples in `samples/hub-*.json`). The Hub accepts
|
||||
versions 1 and 2 (`/api/public-key` lists `[1, 2]`); a v1 request is treated as Cross Events and
|
||||
answered exactly as in sections 1–5. Signing, offline licence checks, update requests and AI calls
|
||||
are as in version 1.
|
||||
|
||||
What version 2 changes:
|
||||
|
||||
| | Version 1 | Version 2 |
|
||||
|---|---|---|
|
||||
| Who | Cross Events | Every product: `product` is `events`, `accounts`, `website` or `app` |
|
||||
| Licence keys | `CEH-…` | `CEH-` Events, `CCA-` Accounts, `CCW-` Website, `CCP-` App |
|
||||
| Register | as §2 | adds `product` (must match the licence: `409 WRONG_PRODUCT`) and `install.publicKey` (the install's Ed25519 public key, PEM) |
|
||||
| Heartbeat | as §3 | adds `product` (must match the install: `409 WRONG_PRODUCT`); `stats` per product (Events as v1; Website: pages, messages, events, editors, `storage`; App: `activeMembers90d` band, modules, prayer coverage; Accounts: accounts, 2FA methods) |
|
||||
| Replies | as §2, §3 | add `linkedProducts`: the church's other installs in use that have a public key, as `{ product, installId, url, publicKey }`, nothing else. Releases carry `requires` (below) |
|
||||
| Bodies | checked by hand | checked against the contract schemas: `400 INVALID_REQUEST` with the field that's wrong; unknown fields are dropped |
|
||||
| Licence token | as §4 | also `customerId` and `product` (still `v: 1`; a v1 install ignores them). `limits.aiMonthlyCapCents` is the church's cap |
|
||||
|
||||
**Releases per product.** An install is told its own product's releases and latest version. A
|
||||
release can need a minimum version of a sibling (`— requires accounts 1.2.0` in its WHATS-NEW.md
|
||||
heading): v2 replies carry it as `requires: [{ product, version }]`, and an update request answers
|
||||
`409 REQUIREMENT_NOT_MET` (with `requirements: [{ product, version, current }]` and a message saying
|
||||
what's needed) until the church's install of that product runs that version or later.
|
||||
|
||||
**AI.** The monthly cap and markup are the church's, shared by all its installs: a call from any of
|
||||
them counts what all of them used this month.
|
||||
|
||||
**Moving an install to v2** (e.g. Cross Events 2.4.0): register again with `protocolVersion: 2`,
|
||||
the product and a public key (same licence and address: same install id, new secret), then send v2
|
||||
heartbeats. Nothing on the Hub needs changing.
|
||||
|
||||
Reference in New Issue
Block a user