Add a README glossary and intended scale; correct the TOKENS comment #184

Merged
lilleman merged 7 commits from item-26 into main 2026-10-31 08:17:39 +01:00
4 changed files with 15 additions and 16 deletions
Showing only changes of commit 25b24a1057 - Show all commits
+1 -1
View File
@@ -370,7 +370,7 @@ docker compose -f compose.yml up --build -d # production
stack up and a *minimal* plugin live. Nothing comes before Quick start. Keep its commands stack up and a *minimal* plugin live. Nothing comes before Quick start. Keep its commands
copy-pasteable; deeper detail lives in its own section, linked. copy-pasteable; deeper detail lives in its own section, linked.
2. **Returning developer (rest).** A **Contents** ToC right after Quick start, then sections ordered 2. **Returning developer (rest).** A **Contents** ToC right after Quick start, then sections ordered
by **what an adopter reaches for first**, not by architectural layering: Goals → Audience → Overview → Users, groups by **what an adopter reaches for first**, not by architectural layering: Goals → Audience → Overview → Glossary → Users, groups
& permissions → Building plugins → menu/blocks/interactivity → Configuration → Auth → Email → & permissions → Building plugins → menu/blocks/interactivity → Configuration → Auth → Email →
Architecture → Testing → Production → Observability → JWT-rotation runbook → Project-layout file Architecture → Testing → Production → Observability → JWT-rotation runbook → Project-layout file
map → Extending. Place a new section by how early an adopter needs it. **Users, groups & map → Extending. Place a new section by how early an adopter needs it. **Users, groups &
+3 -2
View File
@@ -8,8 +8,9 @@ at boot. Entries start at 0.3.0.
### The README defines its terms and states its intended scale ### The README defines its terms and states its intended scale
A new Glossary defines host, manifest, chrome, nav fragment, permission token, system plugin and A new Glossary defines host, plugin, manifest, chrome, nav fragment, permission name, system plugin
denylist. It also states the scale Plainpages is sized for: a handful of plugins per deployment. and denylist. Plugin dependencies also states the scale Plainpages is sized for: a handful of plugins
per deployment.
### The mobile nav stays reachable on engines without `:has()` ### The mobile nav stays reachable on engines without `:has()`
+8 -10
View File
@@ -215,24 +215,19 @@ avoid is shipping a client-side runtime, not using the platform. Markup is seman
- **Host** — the `web` container: the Node process that discovers plugins, serves every page and - **Host** — the `web` container: the Node process that discovers plugins, serves every page and
owns no state of its own. Everything outside it (a plugin's upstream, Ory) is reached over the network. owns no state of its own. Everything outside it (a plugin's upstream, Ory) is reached over the network.
- **Plugin** — a folder under `plugins/` that adds domain screens; the only way to add a feature. - **Plugin** — a folder under `plugins/` that adds domain features.
- **Manifest** — the object a plugin's `index.ts` exports through `definePlugin`: its routes, nav, - **Manifest** — the object a plugin's `plugin.ts` exports as its default: its routes, nav,
permissions and hooks. The host checks it at boot and refuses a bad one ([The manifest](#the-manifest)). permissions and hooks. The host checks it at boot and refuses a bad one ([The manifest](#the-manifest)).
- **Chrome** — everything on a page that isn't the page's own content: brand, menu, user, theme and - **Chrome** — everything on a page that isn't the page's own content: brand, menu, user, theme and
CSRF token. A handler gets it as `ctx.chrome` ([RequestContext](#requestcontext)). CSRF token. A handler gets it as `ctx.chrome` ([RequestContext](#requestcontext)).
- **Nav fragment** — the `nav` entries one plugin contributes. The host merges every plugin's - **Nav fragment** — the `nav` entries one plugin contributes. The host merges every plugin's
fragment into the one global menu and filters it per user ([The menu system](#the-menu-system)). fragment into the one global menu and filters it per user ([The menu system](#the-menu-system)).
- **Permission token** — a `<resource>:<action>` string such as `shifts:write`, granted in Keto and - **Permission name** — a `<resource>:<action>` string such as `scheduling:write`, granted in Keto and
carried in the session JWT's `permissions` claim ([Naming a permission](#naming-a-permission)). carried in the session JWT's `permissions` claim ([Naming a permission](#naming-a-permission)).
- **System plugin** — a plugin that administers Plainpages' own identity stack, through the - **System plugin** — a plugin that administers Plainpages' own identity stack, through the
privileged `ctx.system` ([System capabilities](#system-capabilities-the-ctxsystem-surface)). privileged `ctx.system` ([System capabilities](#system-capabilities-the-ctxsystem-surface)).
- **Denylist** — the optional in-memory list of subjects revoked just now, so a permission change - **Denylist** — the optional in-memory list, per instance, of subjects revoked just now, so a
lands before the JWT expires ([Instant revoke](#instant-revoke-the-optional-denylist)). deactivation or a user's own permission change lands before the JWT expires ([Instant revoke](#instant-revoke-the-optional-denylist)).
**Intended scale.** One deployment serves a handful of plugins, up to about ten, so a plugin's own
dependency tree ([Plugin dependencies](#plugin-dependencies)) costs disk and memory in proportion to
how many are installed. The `web` container scales out because it is stateless ([Stateless
core](#stateless-core)); a catalogue of hundreds of plugins is not a goal.
## Users, groups & permissions ## Users, groups & permissions
@@ -817,6 +812,9 @@ included, since the plugin folder *is* the repo. A baked image needs no extra st
above every plugin, and a plugin resolves it from there — nothing to declare, just import it. A above every plugin, and a plugin resolves it from there — nothing to declare, just import it. A
copy inside your plugin's own `node_modules` would shadow it with a *second* instance of the host's copy inside your plugin's own `node_modules` would shadow it with a *second* instance of the host's
contract, turning a sign-in redirect into a 500, so discovery refuses one there at boot. contract, turning a sign-in redirect into a 500, so discovery refuses one there at boot.
- **Plainpages is sized for a handful of plugins per deployment, about ten.** Each plugin carries
its own dependency tree, so every one adds to disk use and to the memory of the `web` process;
hundreds of plugins is not a goal.
- **The host never upgrades or dedupes your dependencies.** Two plugins depending on the same package - **The host never upgrades or dedupes your dependencies.** Two plugins depending on the same package
each get their own copy at their own version, so neither can break the other by upgrading — and each get their own copy at their own version, so neither can break the other by upgrading — and
keeping yours current, and audited, is yours to own. Renovate here watches every manifest in this keeping yours current, and audited, is yours to own. Renovate here watches every manifest in this
+3 -3
View File
@@ -9,12 +9,12 @@
/* ---------- 1. TOKENS ---------------------------------------- /* ---------- 1. TOKENS ----------------------------------------
Two raw palettes (--l-* light, --d-* dark) are defined once. Two raw palettes (--l-* light, --d-* dark) are defined once.
A small mapping (--bg: var(--l-bg) ...) picks the active set. A small mapping (--bg: var(--l-bg) ...) picks the active set.
Theme resolution (no JS), every rule below needing :has(): Theme resolution (no JS); all three rules below need :has():
• default + @media(dark) → follows the OS (Auto) • default + @media(dark) → follows the OS (Auto)
• html:has(#theme-light) → force light • html:has(#theme-light) → force light
• html:has(#theme-dark) → force dark • html:has(#theme-dark) → force dark
Engines without :has() (Chromium below 105, Firefox below 121) Engines without :has() (Chromium below 105, Firefox below 121,
ignore all three and stay light; see the @supports not Safari below 15.4) ignore all three and stay light; see the @supports not
selector(:has(a)) block further down. selector(:has(a)) block further down.
We avoid light-dark()/color-scheme for app colors because their We avoid light-dark()/color-scheme for app colors because their
resolution is unreliable across engines. color-scheme is still resolution is unreliable across engines. color-scheme is still