Absorb the README's deep-dive content ahead of the README slim-down
The project README is being reduced to a landing page; every fact it
held that the wiki lacked is merged here first (615 lines across 33
pages): the full email-threading & reply-flow design onto Tickets,
directory layout + module-page pattern + file-locations reference onto
Architecture, per-endpoint tables onto API Reference, MFA login flow +
LDAP subsection + injection/audit notes onto Security, the icon library
and editor internals onto Network Mapper, Branding + landing-page and
help/search internals + debug-tool depth onto System, planned objects
onto CMDB, custody/locations/warranty onto Assets, and more.
Also corrects stale claims found during the audit: the Knowledge
audience table still said customer-visible articles had no reader
(the portal Knowledge section shipped), a pre-#903 Portal-Privacy
ambiguity row, the Workflows trigger-wiring section (catalogue spans
every module now), the superseded System module-access bullet, the
Multi-Tenancy users page marked "Planned" for a shipped feature,
and the locale count (21 languages incl. Ukrainian, not 20).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PHP-Enums-and-RBAC: describe the code that exists, not the code that used to
Ed's catch, and a fair one — the page was comparing enums against a string-keyed
design that no longer exists. It quoted four functions that have been deleted
(rbacCapabilities, rbacAllCapabilityKeys, rbacCapabilityExists,
rbacCapabilityLabel), invented a capability that was never shipped
(tickets.manage_settings), and presented all of it under "what we built last week".
A wiki page should describe the actual code.
Rewritten against the codebase as it stands: Cap:: constants, and a registry
DERIVED from each module's settings manifest. Every symbol and every number on the
page verified against the source — 76 capabilities, 76 constants, and the guard's
parameter really is still `string`.
The rewrite also forced an honest walk-back the old page never made. Its central
claim was that enums would collapse four hand-maintained lists (constants,
registry, tab layout, setting-key map) onto one type. THOSE LISTS WERE ALREADY
COLLAPSED — without enums — by deriving the registry from the manifest. So the
headline benefit is banked, and the metadata arguably lives somewhere better: next
to the tab it belongs to, rather than on the type.
What genuinely remains is one narrow thing: a guard whose signature could refuse a
string outright. requireCapabilityJson(string $capability) still accepts one; the
constant makes the right thing easy and a typo loud, but it cannot make the wrong
thing impossible. That is a real gap, and it is now the ONLY one — which is exactly
why the floor stayed at 7.4.
Also corrected two claims on the floor page that had aged with it: it still called
the enum "the only correctness argument on offer" (it isn't — the constants
answered that), and its phase-2 plan still listed the deleted helpers among the
things the swap would remove.
Wiki: split the PHP floor page, refresh Roles & Permissions, restructure the sidebar
The PHP floor page had grown to 861 lines and was three documents in a trench
coat: a decision, a language tour, and a deep argument about permission keys.
Split into three, each standing alone:
Raising-the-PHP-Floor (307) the DECISION — where we are, who breaks, the
verdict, and the constants-to-enums bridge
PHP-8-Language-Features (261) the 101 tour of 8.0/8.1 with before/after from
real code, including the features that WOULDN'T
help us, said plainly
PHP-Enums-and-RBAC (353) the one feature the argument turns on — and the
reason a capability is a Cap:: constant and never
a string. Worth reading even if the floor never
moves, because it explains the permission system.
Section numbers and cross-references rewritten by meaning rather than by
find-and-replace, since the sections changed pages.
Roles & Permissions was stale: written before the manifest became the single
declaration and before the endpoint audit existed. Now says that capabilities are
derived from one file per module (and why the four-list version was the bug), and
carries a table of what D005 has actually found — the shared settings endpoint,
six Intune endpoints, all 49 RFP Builder endpoints, unauthenticated audit-log
forgery, and an LMS learner reaching the authoring settings. Every one found by
hand, by accident, which is the point.
Sidebar: the security pages are now grouped by the three layers they belong to
(module access → capabilities → administrator), rather than a flat list, and all
nine RBAC/PHP pages are linked. The two new PHP pages nest under the floor page.
PHP floor page: note that the registry is now derived from manifests
The code samples in section 3.1 described rbacCapabilities() as current. They
aren't any more — capabilities are Cap:: constants and the registry derives from
the settings manifests. Kept the samples (they show the string-keyed shape the
enum argument is about, which is precisely what the constants replaced) but
flagged them so nobody reads them as today's code.
RBAC: bring the wiki up to date with per-tab capabilities
Both Roles pages predated the per-tab decision and described a system that no
longer exists: rbacCapabilities() (now capRegistry), bare-string guards (now
Cap:: constants), "one manage per module, split later" (we went per-tab), and a
rename that orphans grants and needs a data migration (there's an alias map now).
Neither mentioned the settings manifest, the shared-endpoint key ownership, the
sensitive badge, the umbrella, or the security fix.
Roles & Permissions — rewritten around the per-tab model: why a tab is the unit
(vCenter credentials sit next to a lookup list), the umbrella, sensitive badging,
and a full "how it's enforced" section covering all three mechanisms — panels not
rendered rather than hidden, hard guards on every write endpoint, and per-KEY
authorisation on the shared settings endpoint (including the #829 hole it closed).
Plus what is deliberately NOT guarded, and why gating a read breaks the module.
Roles — Developer Guide — rewritten as the extension guide: why capabilities are
constants (a mistyped string 403s silently and is invisible to admins, who bypass
the check — so the typo becomes a privilege escalation), adding/renaming/removing
a capability, and the full six-step recipe for converting a module, worked against
Asset Management. Now carries the traps that actually bit: a get_* endpoint that
isn't a read (get_vcenter performs the sync and Servers calls it), endpoints with
no guard at all, and the reminder that converting a module is an audit of it.
Plus the three-actor HTTP test matrix and a checklist.
Security — the Authorisation section was wrong, not just stale: it claimed the
System module "cannot be disabled" and is always accessible to active analysts,
which stopped being true when is_admin shipped. Rewritten as the three layers,
each with its gate and its default, and the note that hiding a button is never
the control.
Raising the PHP floor — made the constants-to-enums bridge concrete rather than
asserted. Shows the actual migration: call sites are a symbol rename an IDE can
do and the compiler verifies (a missed one FATALS; you cannot half-finish it),
against hunting 200 string literals where a miss keeps working and a typo 403s
in silence. The real work is confined to one file. Also states the honest
caveat: constants make the typo loud, an enum makes the wrong thing impossible.
Blue sky: raising the PHP floor to 8.1
The case for dropping PHP 7.4/8.0 to unlock enums for the RBAC capability
system, plus a 101 on what PHP 8.0/8.1 actually added, with before/after
examples from real FreeITSM code.
Parked, and the page says why: class constants close the dangerous failure
mode (a typo in a capability key silently 403-ing, invisibly to the admin)
without leaving 7.4. What the enum adds on top is elegance, not safety, and
that doesn't justify breaking every 7.4 user's upgrade path. Documents the
constant-to-enum bridge so the swap stays cheap, and the triggers for
revisiting.
Linked from the Blue Sky hub and the sidebar.