diff --git a/CHANGELOG.md b/CHANGELOG.md index 52a3090..66789d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,8 @@ land on `release/3.1.x` per ### Documentation +- [#457](https://github.com/luadch-ng/luadch/issues/457) - **`docs/PLUGIN_API.md` §8 now enumerates every public plugin export instead of sampling 7 of the 15 that have a public surface.** The old table closed with "See each plugin's source for the full exported surface", so §8 read as a contract but was a sample - during [#447](https://github.com/luadch-ng/luadch/issues/447) PR 6 "not in §8" was mistaken for "dead export" and cost a full re-derivation. The table is now exhaustive (15 rows) and states the completeness contract plus the repo convention that `_`-prefixed exports are internal unit-test / migration seams, deliberately omitted - four plugins (`cmd_gag`, `etc_blocklist`, `etc_whitelist`, `hub_runtime`) export only such seams and appear nowhere, so absence now provably means "exports nothing public". Also fixes a live doc bug: `cmd_ban` exports `bans` as a **table by reference** (mutated in place, per the #238/#239 idiom), not the callable `ban.bans()` the old table showed; the new row documents `ban.bans` + the `ban.bans_path` load-on-demand alternative. Docs-only, no code change. 3.2.x only. + - **new [`CONTRIBUTING.md`](CONTRIBUTING.md) - a contributor entry point, closing a real doc gap.** Three external PRs ([#437](https://github.com/luadch-ng/luadch/pull/437) / [#438](https://github.com/luadch-ng/luadch/pull/438) / [#439](https://github.com/luadch-ng/luadch/pull/439)) all arrived against `master`, and correctly so from the outside: nothing documented the GitFlow-A branch policy, `master` is the repository's default branch (so GitHub offers it), and `README.md` literally described 3.2.x as "active development (`master`)" without mentioning `dev` at all. The new page leads with the branch table (`dev` = target this, `master` = release substrate, `release/3.1.x` = security backports only), records that a mistargeted PR just gets retargeted by a maintainer with the contributor's authorship intact (no close-and-reopen), routes vulnerability reports to [`docs/SECURITY.md`](docs/SECURITY.md), and links out for the rest - build, tests, the core restricted-env `use "X"` contract, the plugin sandbox, the companion scripts repo - rather than duplicating them; the engineering how-to stays in [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md). `README.md` gains a `dev`-branch pointer directly under the release-lines table (the sentence that was actively pointing contributors at the wrong branch) plus `CONTRIBUTING.md` and the previously-unlisted `docs/DEVELOPMENT.md` in its documentation list; `docs/DEVELOPMENT.md`'s orientation table gains a "your first PR" row. Docs-only, no code change. 3.2.x only. - Contributor-doc currency + pattern capture (post-#398 webhook arc). **`CLAUDE.md`:** latest-release marker v3.1.12 -> **v3.1.13**; the §3 module map gains `core/hmac.lua` (HMAC-SHA256, #398) and `core/geoip_update.lua` (was omitted); the §5 current-era note re-framed from "bug triage" to feature-work + triage; §8 gains a "drop-in plugin patch as a lighter alternative to a 3.1.x release" note (the `usr_uptime` v0.10.1 case). **`docs/DEVELOPMENT.md`:** six durable patterns added - `scope="none"` custom-auth HTTP routes, verifying a signed body with `hmac.sha256` + dynamically-named per-instance secret keys, bots-at-module-load / routes-in-`onStart` (reload safety), bulky operator config in its own `cfg/.tbl`, the `getpeername()`-nil-after-accept guard (#401), and poll-don't-fixed-settle for a smoke assertion whose hub state installs asynchronously and fails open (#408). **`docs/HTTP_API.md`:** the §4.4 `none` scope row and the §5.1 plugin-registration `scope` arg now document `scope="none"` for plugin-owned auth (`etc_webhook` is the first user); the `meta` key list gains `plugin`. Plus: two unit tests that existed but were never wired into CI (`etc_blocklist_test`, `cmd_usercleaner_orphan_test` - both pass) are now registered on both smoke legs, so `tests/README`'s "the whole set runs in CI" holds. Docs / test-wiring only. 3.2.x only. diff --git a/docs/PLUGIN_API.md b/docs/PLUGIN_API.md index a34ab70..ba1354b 100644 --- a/docs/PLUGIN_API.md +++ b/docs/PLUGIN_API.md @@ -766,19 +766,41 @@ end ) ## 8. Bundled plugin exports -Plugins commonly imported via `hub.import()`: - -| Plugin | Module | Methods | +Every bundled plugin with a public surface is listed here - this table +is exhaustive, not a sample. A plugin's public API is whatever its +final `return { ... }` exports under a **bare name**, reached via +`hub.import( "" )`. Exports whose name begins with `_` are +internal unit-test or migration seams (the repo convention for "not +API") and are omitted on purpose; four plugins - `cmd_gag`, +`etc_blocklist`, `etc_whitelist`, `hub_runtime` - export **only** such +seams and so appear nowhere below. Absence from this table therefore +means a plugin exports nothing public, not that the doc is behind. + +| Plugin | Import | Public exports | |---|---|---| -| `bot_opchat` | `opchat = hub.import("bot_opchat")` | `opchat.feed( msg )` - send a message to the opchat | -| `bot_regchat` | `regchat = hub.import("bot_regchat")` | `regchat.feed( msg )` - send to regchat | -| `etc_report` | `report = hub.import("etc_report")` | `report.send( activate, hubbot, opchat, level, msg )` | -| `cmd_ban` | `ban = hub.import("cmd_ban")` | `ban.add( user, target, bantime, reason, script )`, `ban.bans()` | -| `usr_uptime` | `uptime = hub.import("usr_uptime")` | `uptime.tbl()` returns user-uptime database | -| `etc_trafficmanager` | `block = hub.import("etc_trafficmanager")` | `block.add( firstnick, scriptname, reason )`, `block.del( firstnick, scriptname )` | -| `etc_hubcommands` | `hubcmd = hub.import("etc_hubcommands")` | `hubcmd.add( cmd_or_list, onbmsg_fn )` - register a `+command` handler | - -See each plugin's source for the full exported surface. +| `bot_opchat` | `opchat = hub.import( "bot_opchat" )` | `opchat.feed( msg )` - send a normal message to the opchat; `opchat.bot` - the opchat bot object | +| `bot_regchat` | `regchat = hub.import( "bot_regchat" )` | `regchat.feed( msg )` - send a normal message to the regchat | +| `cmd_ban` | `ban = hub.import( "cmd_ban" )` | `ban.add( user, target, bantime, reason, script )` (bantime in seconds); `ban.del( target )`; `ban.bans` - the in-memory ban table **by reference** (see note); `ban.bans_path` - the store path for load-on-demand | +| `cmd_help` | `help = hub.import( "cmd_help" )` | `help.reg( title, usage, desc, level )` - register a `+help` entry | +| `etc_aliases` | `al = hub.import( "etc_aliases" )` | `al.resolve( name )` -> alias target or nil; `al.get_aliases_tbl()` -> the alias table | +| `etc_blocklist_feeds` | `feeds = hub.import( "etc_blocklist_feeds" )` | `feeds.get_status()` -> per-feed refresh status | +| `etc_clientblocker` | `cb = hub.import( "etc_clientblocker" )` | `cb.resolve( version_string )` -> block reason or nil; `cb.get_patterns_tbl()` -> the pattern table | +| `etc_geoip` | `geo = hub.import( "etc_geoip" )` | `geo.resolve( country, asn )` -> match string or nil; `geo.classify( ip )` -> country, asn, org; `geo.get_status()` -> policy + DB status | +| `etc_hubcommands` | `hubcmd = hub.import( "etc_hubcommands" )` | `hubcmd.add( cmd_or_list, onbmsg_fn )` - register a `+command` handler; `hubcmd.has( cmd )` -> bool; `hubcmd.list()` -> `{ name, fn }` pairs | +| `etc_msgmanager` | `msg = hub.import( "etc_msgmanager" )` | `msg.get_block_tbl()` -> the message-block table | +| `etc_proxydetect` | `pd = hub.import( "etc_proxydetect" )` | `pd.classify( parsed, ip )` -> matched proxy/VPN types; `pd.get_status()` -> provider + cache status | +| `etc_report` | `report = hub.import( "etc_report" )` | `report.send( activate, hubbot, opchat, level, msg )` - report to ops at or above `level`; `report.broadcast( msg, llevel, ulevel, from, pm )` - broadcast to a level **range** | +| `etc_trafficmanager` | `block = hub.import( "etc_trafficmanager" )` | `block.add( firstnick, scriptname, reason )`; `block.del( firstnick, scriptname )` | +| `etc_usercommands` | `ucmd = hub.import( "etc_usercommands" )` | `ucmd.add( menu, command, params, flags, llevel )` - register a user command; `ucmd.format( menu, command, params, flags, llevel )` -> the UCMD string | +| `usr_uptime` | `uptime = hub.import( "usr_uptime" )` | `uptime.tbl()` -> the user-uptime database table | + +> `cmd_ban.bans` is the live table **by reference** - cmd_ban mutates +> it in place (never rebinds), so a captured `local bans_tbl = +> ban.bans` stays valid; if you rebind your own local instead, that +> local goes stale across `+reload` (the #238/#239 hazard, see +> [`§10`](#10-common-pitfalls)). When you only need a point-in-time +> read, `util.loadtable( ban.bans_path )` at the call site is the +> load-on-demand-safe alternative. ---