Before a plugin may alter the Pensieve, Mediabot now checks who is asking, where the request came from and whether the channel has truly opened the gate.
MB753 introduces the authorization boundary required for future API v3 quote mutations. It separates writing from reading, gives each command invocation a detached caller identity and keeps channel policy, permissions and SQL firmly under core control. The gate is complete—but deliberately unused.
MB747 gave plugins a bounded, read-only view of quote data. MB748 then moved
three pure readers behind that facade with an exact rollback path. The mixed
commands q and quote could not safely follow: depending on their arguments,
they may read, add, delete or update recall counters.
MB753 solves the authority problem before attempting that migration.
data.quotes.write is now a capability of its own, completely separate from
data.quotes.read. A package must request it, an operator must grant it and
the current channel policy must be explicitly on.
No shipped package requests the new capability yet. The historical commands remain core-owned and no live quote was touched during this milestone.
Command invocations now carry an immutable PrincipalV3 built by the core. It
contains only the bounded facts required for authorization:
Plugins receive no password, hostmask, raw IRC message or mutable user object. They cannot change the principal after receiving it.
There is also a less visible but essential seal: every legitimate invocation carries an opaque runtime origin known only to the core. Constructing an object that merely looks like a valid invocation is therefore insufficient. The write sink rejects forged lookalikes before they can reach the data service.
The new core-owned service exposes only two bounded operations:
quote_add(invocation, text)
quote_delete(invocation, id)
The plugin cannot supply a channel, user identifier, access level, SQL statement or database handle. Mediabot derives the channel from the active policy and the identity from the trusted invocation, then performs fixed prepared statements itself.
Quote text must be one non-empty line, at most 512 characters and 2048 UTF-8 bytes. An identical quote already present on the same channel returns its existing identifier instead of creating a duplicate.
Deletion preserves the historical rules: an authenticated author may remove their own quote; Administrators and above may remove one; otherwise the existing channel-level threshold remains authoritative. The final deletion is constrained by both quote identifier and resolved channel identifier.
API v3’s three channel modes keep their precise meaning:
off: the plugin path is inert;observe: plugin code may run as a shadow, but the core suppresses every
mutation before the write service;on: a properly granted and authenticated request may cross the gate.This is an important distinction. Observe mode is useful for comparing dispatch and authorization behavior, but it must never become “almost on” for data changes. MB753 makes that rule structural rather than conventional.
The first final gate did not commit anything. Ten old security tests stopped
the full suite because one new source comment wrote the mode name with Markdown
backticks. Those tests intentionally treat any backtick in
PluginManager.pm as possible shell execution, so all ten reported the same
lexical false positive.
The gate behaved exactly as designed: 20,001 tests passed, 10 failed, and no commit or push occurred.
R2 removed only that comment formatting. The ten historical guards then passed 163/163, the MB753 target set passed 608/608, and the fast lane passed 7,604/7,604. The corrected 22-file scope was re-fingerprinted before a fresh final gate.
That tiny incident is worth recording. A safety gate that occasionally objects to punctuation is mildly annoying; a safety gate that silently waves a suspicious change through is useless.
The successful complete suite ran once on R2, immediately before commit, with
visible progress. The staged digest remained identical before and after the
suite. The development service kept the same PID and restart count, and the
repository finished clean and synchronized with origin/master.
MB753 was committed and pushed as
8e936905a3ee595dc6512bdd261f7b597e030e54:
MB753: guard quote writes at the Ministry authorization desk 🪶🏛️
Database-backed plugins should not become trusted merely because they run inside the same process. They need narrow capabilities, detached identities, fixed operations and authorization decisions that remain owned by the core.
MB753 provides that foundation without rushing the visible migration. The
next quote milestone can now evaluate reversible q and quote adoption
against a real, inert write gate—with observe-first evidence and an exact
fallback still available.
The Pensieve remains unchanged. The authorization desk is staffed. Future spells will have to present the right seal. 🏛️
Mediabot v3 development milestone MB753 · 20 September 2026
You must be logged in to reply.