Kopling's source of truth — our mission, our beliefs, how we're governed, and the technical choices behind the product, all changed openly as we go. Written for two readers at once: the people building and using Kopling, and any AI assistant helping with it.
Kopling exists so human interaction can grow into honest, real relationships — through work, hobbies, friendship, or a stranger's well-intended answer at the right moment.
Connection is the goal. The rest of this page — including the whole technical stack — exists to serve that goal, not the other way around:
Our settled beliefs about the product. Every feature is checked against these six.
In this charter, in our docs, in our product, and in the UI, we say "people" — because the person behind an account has real emotions, struggles, and hopes. This applies to all writing about Kopling.
Engagement is what happens when people enjoy talking to each other — it's a result, not something we chase with tricks. We won't order feeds to keep people scrolling, send notifications just to pull people back, or reward outrage. We track whether questions get answered and new people feel welcome — not how much time people spend on the site.
A quick "this is cool" is a real response, and it should be effortless to send. It exists to make the person who posted feel seen — not to become a public score to chase. Getting this right is a priority from day one, not something we bolt on later.
A single honest, fair, well-meant answer from a stranger counts as real connection. Support communities belong in our mission just as much as friend groups do — which is why our mission doesn't use words that imply closeness or a long history together.
Someone venting on a bad day is coping, not breaking a rule — what we act on is harm to other people, not the bad day itself. We assume good intent by default, and we design for the rare exception without treating everyone like a suspect. People who are removed for harm can still come back with dignity. Spam and bots aren't a rare problem — they happen constantly, especially once other communities can connect to ours — so stopping them is part of the core software from day one, not something added later.
A community works because people show up for each other. AI is never a member of that community — it never pretends to be a person, and it's never used to fake warmth or activity. It's welcome behind the scenes, wherever it genuinely helps: summarizing long threads, translating, helping moderators sort through reports, helping admins write things. Every use is judged by one question: does this actually help the people involved, or does it get in their way? Whenever AI is involved, that's stated plainly. Admins decide how much AI their own community uses. Extensions that make AI pretend to be a person are not allowed in the registry.
Kopling runs on a BDFL model — "benevolent dictator for life," the same pattern behind Laravel, Linux, and Ghost: one named person holds final say, backed by real public promises. It's a deliberate choice for where the project is today, not a placeholder for something better later — a single decision-maker can commit to a direction and change course without waiting on a vote, which is exactly what a project this early needs.
Two things matter most from here: how a decision actually gets made today, and what happens so the project never depends on one person forever.
Decisions get proposed in public, people get a real window to object, and the final choice — plus the reasons for it — gets written down.
Before we reach version 1.0, we'll publish a plan for what happens to the code, the trademark, the extension registry, and the infrastructure if the founder can no longer lead.
The code is free, and stays free. The right to copy it and continue it elsewhere — a "fork" — is the community's real safeguard, and it keeps any leader honest, including this one. We won't quietly make the code closed later, and we won't switch to a license that only looks open.
Sponsors and donors get thanks, visibility, and a voice we genuinely listen to — never control over technical decisions, moderation, or the roadmap. Money and decision-making stay separate, and we publish our finances.
Decisions, the reasons behind them, and the project's money are public. We keep something private only for a short, clearly named reason — someone's personal privacy, an active legal matter, or a security issue before it's fixed — never as a habit.
We aim to give our Technical Steering Committee real authority once it exists, not just an advisory voice — the final say over the extension API, how breaking changes are handled, and how old features get retired. It starts small and takes on more as the group of contributors grows; it may begin as advisory and later become binding.
Right now decisions move fast and often, while the software is still finding its shape. That's temporary by design: we aim to hand day-to-day control off to a public, two-monthly release cycle with a clear roadmap (Section 7) once the software is stable enough that changes slow down and a Technical Steering Committee has formed. Exact trigger conditions are still open (see Open Questions).
Daniël Klabbers owns Kopling today — the extension registry, and anything Kopling ever sells or hosts. The trademark isn't registered yet — that costs money the project doesn't have yet (see Open Questions) — and will be owned the same way once it is. The code itself stays free to use, copy, and run no matter who owns the business around it (Section 3). Starting with one owner is a deliberate choice, not something to apologize for: it's simpler, faster to act on, and honest about where the project actually is right now. This document is, and is meant to stay, Kopling's entire system of governance.
Paying people for the maintenance work they actually do is a goal, not something to feel awkward about — today that's the founder, who is currently also the project's only active maintainer; as that changes, so does who gets paid for it. This is about compensating work, not ownership — owning Kopling isn't itself a paid role. Research on open source sustainability — Tidelift's maintainer surveys among others — consistently finds that paid maintainers get more of the critical security and maintenance work done, and stay at it longer. Kopling is built around that finding from the start. Possible sources of income: sponsorship and donations, hosting, registry services, and paid support.
This charter is one single, board-free document that gets updated openly — no board is needed to change it. It comes with a public Decision Log and a list of options we considered and rejected, both explained honestly, and it's written in plain language for readers around the world. Kopling's own decisions are made and written down using Kopling itself.
This section describes the kinds of role in the project — not a list of exactly who holds what today. Who actually holds each role changes constantly, so that list lives in a separate, freely-updatable document, not in a charter that would need editing every time someone joins or steps back.
This is the authority ladder — how much say a role carries. It's a separate question from what field of work a role is in, which is what Divisions, below, describe. Some of these roles are global — they don't belong to any one division. Others only exist inside a division's team, and mean nothing without one: a "team lead" only means something once you know which team.
A division is a field of work, not a level of authority — it's the answer to "what is this role even about." Every standing team (below) sits inside exactly one division. Each team lists its responsibilities: concrete, checkable duties, not a claim about territory — overlap between teams is normal and expected, so drawing hard boundaries would just be dishonest about how the work actually happens. What matters is whether a responsibility got done, since that's the only fair thing to check against (Section 7 covers what happens when it doesn't).
Keeps the people who use Kopling safe, heard, and helped.
Builds and maintains the software itself.
kopling/core and the skeletonKopling\Extend\* contracts stable across releasesShapes how Kopling looks, feels, and behaves — the shared visual and interaction language every surface builds on.
<x-k::*> Blade component library — props, behavior, and accessibility contracts (Section 9, Styling)Makes Kopling understandable, in people's own language.
Tells people Kopling exists, and keeps it funded.
A role with nobody in it is an empty seat, not a problem to hide. Right now, every role except founder is empty, and we say so plainly here instead of pretending otherwise. Naming an empty role is what eventually lets the right person step into it — nobody can be recruited for a role they don't know exists.
Once there's more than one contributor, the specific people holding roles below team lead — and which roles are still empty — will be listed in a separate, living document called Teams and Roles, published on kopl.ing or wherever Kopling's community lives. That list can change any day without ever needing to change this charter.
A standing team does ongoing work — like moderation or translation — and continues for as long as that work is needed; it lives inside one of the four divisions above. A working group exists for one specific task with a clear end point (for example, writing the first version of the extension API), closes once that task is done, and doesn't need a permanent division of its own — it can form inside one division or draw people from several. None exist yet — we describe the difference now so the shape is settled before the first one forms.
If one person holds several important roles at once, that's tracked as a risk (Section 7), not praised as efficiency. Right now, the founder holds every role, simply because there's no one else yet. That's stated plainly as something to reduce over time, not as how things are meant to stay.
This section states, now, before there's any inner circle to keep people out of, how someone earns standing in the project. It's a clear rejection of closed, invite-only communities: projects that only let a small circle in tend to stop growing, and burn out the few people left inside.
Anyone can earn standing in the project through real contribution. No existing group gets to hand-pick who's allowed to matter.
Writing code counts. So does answering questions, writing docs, translating, moderating, and reviewing other people's work. Counting code alone misses most of what actually keeps a project alive, and makes the people doing that other work invisible twice over — once in the work itself, and again when nobody credits it.
Recognition is based on facts a person can point to in their own history — never on secret rules, and never on an algorithm quietly judging the "quality" of someone's contribution. There's also a simple, human way to appeal if the rules get an edge case wrong — which matters most for someone who doesn't write English as a first language, or who contributes quietly rather than loudly.
Right now, recognition means contributors are named, credited, and thanked publicly — it doesn't yet come with a vote, because there's no elected group to vote for. Once the Technical Steering Committee (Section 3) exists, voting rights will be built on this exact same ladder, decided here rather than invented separately later. Deciding this now means the ladder gets designed once, instead of rebuilt under pressure once elections become real.
Kopling's health depends on the wellbeing of the people keeping it running — today, mainly its founder; over time, its contributors. These rules protect the project's structure, not any one person's willpower: making rest possible is the project's job, and we say so now, before growth makes it urgent.
Holding a role never means being available all the time. It's normal for anyone's involvement to go up and down — that includes the founder today, and it will include every future contributor.
As Kopling grows a contributor base, everyone's standing is described honestly using one of three states: active (the normal state), reduced-activity — a pause anyone can take at any time, no explanation required, that pauses their duties but keeps their role and standing exactly as they were — and emeritus, which permanently honors someone's past work with no ongoing duties attached, and can always be reversed if they come back. Public lists always show who's genuinely active, instead of quietly keeping inactive people listed as if they still were.
Kopling aims to release on a steady, public schedule — maybe every two months, though the exact number isn't fixed yet (see Open Questions). A steady schedule does two useful things. First, it gives an honest, no-blame way to notice when someone has quietly become less active, and move them to reduced-activity or emeritus — as a simple, scheduled check, not as anyone passing judgment. Second, it keeps releases honest: each one ships whatever is actually ready, and anything unfinished waits for the next cycle instead of being rushed. Delays happen no matter how good the plan is, and good code takes the time it takes — the schedule exists to make that normal, not something to feel guilty about.
Reduced-activity and emeritus, above, describe a person's overall standing — always available, no explanation required, never a judgment. A role is a narrower thing: one specific seat, held because a division's team (Section 5) needs it filled, with responsibilities that are concrete and checkable on purpose — not to draw territory, but because a responsibility either got done or it didn't, and that's the only fair thing to check. At the same release-cadence checkpoint used to review activity states, an unmet responsibility gets the same no-blame first move: reduced-activity for that role, no explanation required. If the seat is still unfilled through a full cycle with nobody responding, it's simply named vacant again — the "empty roles are named, not hidden" rule (Section 5) applies here too. Losing a role this way never touches someone's standing on the recognition ladder (Section 6) — that's for what they already contributed, not what they're doing right now. This is deliberately separate from removal for harm (Section 2, item 5), which is about conduct, not output, and follows moderation's own process, not this one.
A "bus factor" is a simple way of asking: if one person disappeared tomorrow, how much would the project lose? Kopling keeps an honest, current list of every place where that number is one — a single person holding critical knowledge alone — each with a plan to fix it, whether that means teaching someone else, writing things down, or, when there's genuinely no other option, accepting the risk on the record. Right now, the list has exactly one entry: the whole project. The plan to fix it is the succession plan (Section 4) and the future Technical Steering Committee (Section 3), whose job partly exists to end this on a real timeline, not by accident. Once Kopling has a public home to publish it (Section 8), this register becomes public too.
Some work never really finishes — responding to security issues, managing releases, sorting incoming reports, keeping dependencies up to date. This kind of work is where burnout usually starts, because there's no natural point where it's "done." Other work — building a new feature — is optional and has a clear finish line. Once more than one person shares the ongoing work, it will rotate through a duty roster: one named person covers it for a fixed period, the schedule is public so any gaps are visible, and nobody outside their turn has any obligation at all. Until then, the founder carries all of it alone — stated here plainly as something that can't stay that way forever, not as how things are meant to work.
Every role in Section 5 comes with a written list of concrete responsibilities — not a vague claim to "everything nobody else is doing," which can never actually be finished, and never lets the person doing it feel done. Work that falls outside every defined role's responsibilities is the Technical Steering Committee's job to assign somewhere, once one exists — it shouldn't just land on whoever feels most responsible for it.
Turning down an idea, or simply moving slowly, is normal and legitimate. It reflects badly on nobody — not the person who suggested it, and not the person who said no. Moving slowly and sustainably is better than moving fast and burning out.
Standing under Section 6 counts every kind of work that keeps the project running — not just code. Work that goes unseen and unthanked burns people out faster than work that gets valued.
People trust how Kopling is governed because they can see it happen — not just because we say it's well-meant. Governance that happens out of sight loses that trust no matter how good the decisions actually are. This section explains, concretely, how the "public by default" promise (Section 3) works.
Decisions, the reasons behind them, and the project's money are made and written down in public. We keep something private only for a short, clearly named reason: something about a specific person, an active legal matter, or a security issue before it's fixed. Each of these has a time limit, and once it passes, a public summary follows.
Sponsorship and donations aren't just summarized in a periodic report — every individual contribution and every expense is visible from the moment it happens, on the public ledger Kopling uses for money (currently Open Collective). Anyone can see who gave what, and where it went, without waiting for a scheduled report. This is a stronger promise than a report on a schedule (see "How often we report," below), and it's chosen on purpose: real-time visibility is harder to quietly walk back later than a periodic summary would be.
Day-to-day conversation can happen anywhere. What has to end up public is the record of each decision — what was proposed, why, and what was decided — and that record lives on a real Kopling community, the same way any community's own governance would live on the platform it governs. It's a small but real promise: we use Kopling to govern Kopling, which shows we trust the tool, rather than just saying we do.
The moment any group beyond the founder has real authority — a Technical Steering Committee, a team lead, anyone who could personally benefit from a decision — their conflicts of interest go into a register anyone can see. A conflict shared only inside a private meeting is a courtesy, not real accountability; this public register is what makes Sections 6 and 7 mean something once more than one person is involved.
Financial reports and major decisions are published on a regular, named schedule. The exact schedule for each type of report isn't fixed yet (see Open Questions) — but committing to a regular schedule, instead of an occasional one, is already decided.
Settled architecture. Recurring pattern applied throughout: mainstream tool inside, sovereign Kopling contract outside — so a dependency's major version becomes a Kopling minor, never an ecosystem-wide break.
Full laravel/framework — never cherry-picked illuminate components. Kopling ships its own skeleton. Two-tier extension API: Kopling\Extend\* contracts + blessed Laravel primitives (Eloquent, collections, validation, events); framework internals stay core-only. Laravel majors adopted on Kopling's schedule.
The thing people share and gather around is called a Moment — not a "Discussion" or "Topic," and there's no separate "Original Post"/"First Post" concept sitting inside it: a Moment already is the whole thing, the sharing and everything discussed, reacted to, praised, or loathed around it. An individual contribution inside a Moment stays a plain reply — no renaming needed there.
htmx 4 + Blade + Alpine. Server is the single source of truth; no build step for extensions. Raw hx-* is not the official API — Kopling Blade components (<x-k::action>) emit it. Core-owned JS islands are custom elements so they survive swaps and morphs.
UI is organized into named Portals — Community and Admin today, more addable later (a Moderation portal for large communities, serving the Moderation team of Section 5, is the target proof case) — never "Frontends," which reads as JS-framework terminology in a stack like this one. Permission is never hardcoded to a Portal or to an "admin" flag: every action checks its own named, granular permission, and "Admin"/"Moderator" are conventional bundles of those permissions an admin assigns — not a special case the code treats differently. A Portal is a registrable pattern, not fixed to exactly two. The Admin Portal proves it: it ships as its own extension, bundled by default so every install has one, but an operator can remove it entirely from their composer.json — a CI/CD-deployed install, or a large community running its own moderation-only panel instead. Nothing about "the admin panel" is hardcoded into Core.
"An extension is PHP and templates. Full stop." Named, prioritized outlets across small granular partials; extensions ship routes returning HTML fragments; out-of-band swaps update core regions through the response, never by patching. Outlets compose; overrides don't — overrides require manifest-declared conflict detection.
Tiptap; ProseMirror JSON is canonical; server-rendered via tiptap-php through a whitelist — the same whitelist doubles as the XSS and federation-sanitization model. Extension nodes ship as a PHP renderer + optional plain ESM file, no bundler required. v1 ships core nodes only; the extension node API waits until proven by 2–3 first-party extensions.
Tailwind v4 + daisyUI beneath a Kopling-owned Blade component library — the only official styling API (~25–30 components with behavior and accessibility built in). Runtime CSS-variable theming lets admins re-brand without a rebuild.
UUIDv7 everywhere; federated IDs are URIs with the UUID as local component. Every federatable row carries UUID + origin from migration #1 — remote content is first-class, never a shadow table. Non-negotiable: near-impossible to retrofit later.
ActivityPub with forum-shaped conversation semantics, following the Discourse × NodeBB path. Delivery is a queue problem; remote media caching consumes the storage system below. Federation may ship post-1.0; schema readiness ships day one.
Graceful degradation: runs anywhere (sync queue, polling/SSE fallback, cron), shines on real infrastructure (Reverb, real queues). Capability detection is first-class — an admin health dashboard names what's degraded and why. Degradation is visible, never silent.
SSE + WebSockets via htmx 4's first-party extensions; pushed fragments render through Blade, so they carry every extension's outlets automatically. PWA-first with Web Push at launch; a Hotwire-Native-style webview shell is the hedge if iOS friction proves costly.
MySQL, MariaDB, Postgres, SQLite. SQL Server explicitly unsupported — every extra engine taxes every extension author forever. SQLite is strategic: trivial local dev and a genuine small-community tier.
v1 ships database LIKE behind a driver interface. The indexing pipeline — extensions registering searchable content types — is part of the contract from day one, since that's the part that's hard to retrofit.
Capability-based named filesystems: extensions declare required capabilities (public URL, signed URL, streaming, cloud); admins define named instances and map requests to them. A thin layer over Flysystem.
kopl.ing, Packagist-protocol-compatible — Composer resolution for free, registry sovereignty retained. Machine-checked compatibility metadata (core constraints, DB engines, declared overrides) and package signing from day one.
Core-team-owned official infrastructure, never one volunteer's side project. Self-hosted Weblate or Tolgee, SSO'd to kopling.org; an automated pipeline carries strings from the registry to translators and back as installable language packs.
Webhooks before a JSON API — cheaper, unblocks automation and bridges sooner. The API itself is deliberately deferred, kept cheap later by keeping controllers thin over action classes.
Agentic development can only be acceptable when properly reviewed, use AI as a planning assistant, and toolbox, not a replacement for a developer.
Kopling's look: Delft blue on a light background, with orange used only for the "follow" and "sponsor" buttons, and a wordmark shaped like a clutch. Its voice: confident but calm, inviting disagreement instead of shutting it down — decisions are published before the code is written, so people can challenge them early.
Plain language for a worldwide audience: Kopling's audience is worldwide, and most people reading this charter, using the docs, or using the product do not speak English as a first language. We use plain, direct words instead of jargon or formal governance language wherever a simple word will do — including in this charter itself. When a technical or legal term genuinely can't be avoided, we explain it in plain words the first time it appears. This isn't about making things simpler for its own sake — it's about not shutting people out. A charter only insiders can understand would quietly recreate the exact elitism Section 6 exists to reject.
We expect to be copied, and that's fine: our license lets anyone copy this whole plan and build it themselves — that's the point of an open license. But the plan was never the hard part to copy. What can't be copied is a multi-decade career spanning enterprise engineering leadership and open source stewardship, the registry and translation infrastructure, and trust earned over time — none of which comes bundled with the code.
Public identity: Daniël Klabbers · github.com/luceos · linkedin.com/in/luceos
The backlog. Nothing here is decided — reference it, don't assume it.
Kopling\Extend\* contract list.<x-k::*> component inventory (props, ~25–30 components).kopling/reactions proves the extension system before its API freezes.Abridged. Each row: what was decided, what it beat, and why.
| # | Decision | Over | Key rationale |
|---|---|---|---|
| D1 | Full Laravel framework | Cherry-picked illuminate; Symfony | Avoids bespoke-kernel maintenance tax; common stack; ecosystem access |
| D2 | htmx 4 + Blade + Alpine | Livewire; Vue islands; pure Blade | Server-rendered is most extensible; avoids build-step disease |
| D3 | Component wrapper over raw hx-* | Raw htmx as the API | An htmx major becomes a Kopling minor |
| D4 | Tiptap, JSON-canonical, PHP-rendered | Markdown/BBCode; client-rendered HTML | One whitelist covers XSS and federation; no-build ESM nodes |
| D5 | Tailwind v4 + daisyUI under <x-k::*> | Raw Tailwind; Basecoat; Bootstrap | Avoids the JIT trap; runtime theming; behavior lives in components |
| D6 | UUIDv7 + origin-aware schema | Auto-increment ints; UUIDv4 | Index locality; federation-ready URIs; can't retrofit later |
| D7 | ActivityPub, forum-shaped | AT Protocol; microblog semantics | Discourse × NodeBB path; direct prior participation |
| D8 | Graceful-degradation hosting | VPS-only; hosted-only | Serves both small-community and enterprise ends honestly |
| D9 | kopl.ing registry, Packagist-compatible, signed | Raw Packagist; bespoke protocol | Free Composer resolution with retained sovereignty |
| D10 | 4 databases, no SQL Server | Every engine Laravel supports | Each extra engine taxes every extension author forever |
| D11 | Core-owned translations, self-hosted platform | Build a bespoke platform; leave it volunteer-run | Bus-factor-1 was the actual failure, not the tooling |
| D12 | Founder-led + guarantees + TSC | Community ownership; foundation-first | A single decision-maker moves faster and stays more accountable at this stage than diffuse ownership; TSC is the deliberate path to broadening it over time |
| D13 | PWA-first, webview shell as hedge | Native apps; API-first mobile | HTML-over-the-wire makes the shell path free later |
| D14 | Webhooks before a JSON API | API-first | Cheaper now; thin action classes keep it cheap later too |
| D15 | Connection is the mission; immediacy is a means | Immediacy-first framing | Immediacy only matters because it serves authenticity |
| D16 | Engagement is a consequence, never a KPI | Engagement optimization | Optimizing it manufactures counterfeit enjoyment |
| D17 | Moderation addresses harm, not humanity; anti-abuse is core v1 | Venting-as-violation; spam-as-exception | Bad days are coping, not offending; spam/bots are weather |
| D18 | AI serves people, never replaces them — visible, admin-controlled | Blanket AI ban; AI as participant | The human following is the actual success condition |
| D19 | "People, not users" vocabulary | Industry-default "users" | The person behind the account has emotions, struggles, hopes |
| D20 | Open and earned standing, never gated by an in-group | Closed-core / invite-only contributor model | Closed pools fail to grow and concentrate burnout on who's left |
| D21 | Contribution defined broadly (code, triage, docs, translation, moderation, review) | Code-only recognition | Fixes the invisible-labor gap; recognition should match what actually sustains the project |
| D22 | Roles are kinds, not a fixed org chart; vacancy named openly; specifics live in a separate Teams and Roles document | Enumerating every named role in the charter itself | Charter stays stable; the team page stays current; an unfilled role is a recruitment target, not an embarrassment |
| D23 | Voting standing deferred until a TSC exists, but built on the same contribution ladder as recognition | Inventing a separate electorate definition later | Designing one ladder now avoids redesigning it under pressure once elections become real |
| D24 | Steady, named release cadence adopted as a principle (candidate: every two months) | Release-whenever-ready with no rhythm | Gives an honest, low-drama signal for activity states; forces ship-what's-finished scope discipline |
| D25 | Plain language for a global, largely non-native-English audience, applied to the charter itself | Governance-register or jargon-heavy drafting | Inclusion, not simplification for its own sake — avoids recreating elitism through inaccessible language |
| D26 | "Moment" as the core content-model noun; "reply" stays a plain, unrenamed word | "Discussion"/"Topic" + a separate "Original Post"/"First Post" | Continues language the charter already uses for the Immediate principle instead of inventing separate jargon; removing the OP/first-post split entirely — a Moment already is the whole thing — costs nothing since it was never load-bearing |
| D27 | Section 5 splits "kinds of role" (authority: founder, TSC, team lead, team member, contributor at large) from "divisions" (field of work: Community, Development, Content, Growth), each with named standing teams | One flat role list mixing authority and field of work; a large WordPress-style team roster (~20+ Make teams) | Authority and field of work are different questions — "team lead" is meaningless without knowing which team; four divisions matches the scale of Rust's ~8 top-level teams, appropriate for a pre-1.0 project with one contributor today |
| D28 | Teams are defined by checkable responsibilities, not covers/doesn't-cover boundaries; a role-removal mechanic (the seat goes vacant, not the person) sits in Section 7 alongside reduced-activity/emeritus | Covers/doesn't-cover boundary language; no mechanism for an unmet responsibility beyond the general activity-state check | Boundaries imply no overlap, which isn't true in practice — a checkable duty is what's fair to measure; underperformance and burnout need different no-blame responses, and losing a seat shouldn't cost someone standing already earned (Section 6) |
| D29 | UI organized into named "Portals" (not "Frontends"), gated by granular per-action permissions instead of a hardcoded admin flag; a Portal is a registrable pattern, not fixed to exactly two | "Frontend" terminology; a binary admin-access flag; hardcoding Community/Admin as the only two surfaces forever | A binary admin flag allows no partial admin access; granular per-action permissions matter most for support/staffing communities (Section 2, item 4); proving Portals are generatable via a real Moderation portal mirrors how a first-party extension proves the extension API before it freezes |
| D30 | Added a fifth division, "Design & UX", with one team (Design system team) | Folding design-system/theming work into Development or Growth | Design-system and theming work (<x-k::*>, daisyUI, brand tokens) is real, ongoing, and distinct from either building the backend or marketing the product — naming it gives it a real home instead of hiding it inside an unrelated division |
| D31 | Extended D28's "checkable responsibilities, not covers/doesn't-cover" language to the Kinds-of-role ladder, and split it into global roles (Founder, TSC member, Contributor at large) versus division-specific roles (Team lead, Team member) | Covers/doesn't-cover phrasing surviving only in the authority ladder; one flat list mixing global and division-scoped roles | D28 fixed the language for named teams but missed the ladder itself; grouping by scope makes explicit which roles need a division to mean anything (team lead, team member) and which don't (founder, TSC member, contributor at large) |
| D32 | Sponsorship and donations run through Open Collective, chosen specifically because every contribution and expense is public in real time, not only in a periodic report | A private payment processor with only scheduled financial reports | Real-time, per-transaction visibility is a stronger and harder-to-quietly-walk-back promise than a periodic summary; matches "public by default" (Section 3) down at the level of individual money movements, not just aggregate totals |
| D33 | Admin Portal ships as its own extension (kopling/admin), bundled by default but removable via composer.json — nothing about it is hardcoded into Core | Admin panel as a permanent, undetachable part of Core | Proves a Portal is genuinely a registrable pattern (D29), not just in theory; lets a CI/CD-deployed install or a very large community drop the admin panel entirely and run their own moderation-only panel with fewer features instead. CannotBeDisabled still stops a signed-in admin from toggling it off inside a live install — a narrower, separate guarantee from an operator's own install-time choice |
This document is meant to be questioned. Read it, challenge it openly, and when something changes, update it on purpose: write down the change, keep the reason, and note what it replaces — never rewrite it quietly.