Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<name>.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.
Expand Down
46 changes: 34 additions & 12 deletions docs/PLUGIN_API.md
Original file line number Diff line number Diff line change
Expand Up @@ -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( "<plugin>" )`. 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.

---

Expand Down
Loading