Skip to content

Releases: KingPin/Garrul

v1.22.1

Choose a tag to compare

@github-actions github-actions released this 01 Aug 04:38

Fixes: /admin/usage rejected valid Cloudflare API tokens.

If your usage dashboard showed "The CF_API_TOKEN currently configured did not validate" with http_401, this release is the fix — and your token was almost certainly fine.

Cloudflare issues two kinds of API token, each verifiable only at its own endpoint. Garrul checked /user/tokens/verify exclusively, so account-owned tokens (the cfat-prefixed kind the current dashboard hands out) always came back 401, no matter how valid or correctly scoped they were. The page failed closed before ever reaching the analytics queries, which would have worked.

Garrul now probes /accounts/{id}/tokens/verify first and falls back to the user endpoint, so both token kinds verify. If both probes fail, the error names each one instead of showing a single ambiguous code.

Notes:

  • No config changes. No new env vars, no migrations — deploy and reload /admin/usage.
  • If you re-created a token while chasing this, the old one likely still works; nothing here requires rotating it.
  • The token-error page previously blamed a wrong CF_ACCOUNT_ID or a dropped scope. Neither can cause it: the account endpoint verifies the bearer alone and ignores the ID in its path, so a wrong account ID passes verification and instead surfaces as a per-panel account_not_found. The copy now says so.

v1.22.0

Choose a tag to compare

@github-actions github-actions released this 31 Jul 20:56

Highlights

  • Four anti-spam / editing dials are now tunable from the admin panel — edit window, spam link threshold, honeypot minimum fill time, and first-comment moderation. Each resolves DB override > env var > built-in default, so your existing wrangler.toml values keep working as the deploy-time default and "Reset to defaults" hands control back to the env. Moderation tab gains an "Anti-spam heuristics" card; the four rows they replace leave the read-only Configuration table.
  • setup.sh no longer misassigns KV namespace ids. Ids were substituted into the first remaining placeholder, so if you had reordered or hand-edited the [[kv_namespaces]] blocks in your wrangler.toml and re-ran setup, every id could land under the wrong binding — setup reported success while the Worker read sessions out of the rate-limit namespace. Ids are now matched on binding name, and a block with no matching binding warns loudly instead of touching an unrelated one.
  • setup.sh runs on macOS/BSD. The D1 id substitution used GNU-only sed -i, which on BSD sed read the expression as a backup suffix and failed with invalid command code, leaving the placeholder in place. No in-place sed remains in the script.

Notes

  • The default edit window was split 5 minutes (widget) vs 15 (server); both are now 15. A window of 0 disables editing outright. Set EDIT_WINDOW_MINUTES explicitly if you were relying on the old widget-side value.
  • SPAM_LINK_THRESHOLD stays tri-state: -1 disables the check, 0 flags any comment containing a link, N flags more than N.
  • Enabling the honeypot fill-time dial without SPAM_FORM_TS_SECRET now shows an inline warning in the admin UI rather than leaving the check silently inert.
  • wrangler.toml writes go through an atomic rename, and setup's "Next steps" list now names all four placeholder vars (ALLOWED_ORIGINS, ADMIN_EMAILS, PUBLIC_BASE_URL, OAUTH_CALLBACK_BASE) instead of three.
  • No new env vars, no new secrets, and no D1 migration. Upgrading is a redeploy.

v1.21.0

Choose a tag to compare

@github-actions github-actions released this 30 Jul 20:09

Config lived in six hand-maintained lists that drifted apart: secrets.example.env, .dev.vars.example, wrangler.example.toml, scripts/setup.sh, the AGENTS-OPERATE §5 table, and release-manifest.json. This release makes one registry the source of truth for all 53 entries and adds a bulk path for setting secrets. Closes #42.

Highlights

  • Bulk secrets. cp secrets.example.env secrets.env, uncomment what you have, then npx wrangler secret bulk secrets.env && rm secrets.env — one call instead of ~24 interactive wrangler secret put prompts. secrets*.env is gitignored. Leave anything you don't have commented: wrangler treats an uncommented empty value as a real, empty secret and will overwrite a live key with nothing.
  • scripts/setup.sh now offers bulk or one-prompt-per-secret, and covers all 23 secrets. It previously prompted for 16 — CF_API_TOKEN, GITHUB_TOKEN, SPAM_FORM_TS_SECRET and the AKISMET_* pair were never offered. The six ID+secret pairs (Turnstile and the five OAuth providers) collapse behind one prompt each; Telegram's two secrets prompt separately, since the bot token alone enables outbound notifications.
  • One registry. scripts/config-registry.ts holds all 53 entries (23 secrets, 30 vars). secrets.example.env, .dev.vars.example, wrangler.example.toml, setup.sh's prompt lists and the AGENTS-OPERATE §5 table are generated by npm run config:build; npm run config:check fails CI on drift.
  • New optional secret: GITHUB_TOKEN — a read-only public_repo token that raises the 60 req/hr GitHub API cap used by update checks. Nothing breaks without it.

Fixes

  • npm run upgrade now reports optional secrets added since your installed release. They were previously invisible, passing neither the required-only secret diff nor the vars-only "new settings" filter.
  • "New settings" is keyed on addedIn rather than a manifest set-difference, so upgrading from an older install no longer lists settings you already have.
  • wrangler.example.toml's [secrets] required = [...] block ships commented out, with the reason inline: declaring it makes wrangler dev bind only the listed names out of .dev.vars and silently drop every other secret, so local OAuth, Resend, Akismet and Telegram read as unconfigured.
  • .dev.vars.example again points at Cloudflare's always-pass Turnstile test keys for local dev.
  • GITHUB_TOKEN is declared in the Bindings type.

Notes

  • No D1 migrations and no new required secrets — upgrading is redeploy-only. Run npm run upgrade to see the plan for your install.
  • The Node.js 24 floor from 1.20.0 still appears in the upgrade plan's breaking-changes section. If you are already on 1.20.0 or later there is nothing to do; the entry is not yet version-scoped.

v1.20.0

Choose a tag to compare

@github-actions github-actions released this 30 Jul 04:13

Requires action before upgrading

Node.js 24 is now the minimum. Building or deploying on Node 22 fails at npm ci. Upgrade your build/deploy host first — the repo's .nvmrc now reads 24:

nvm install 24 && nvm use 24
npm ci          # re-run after switching; a node_modules built on 22 can carry incompatible binaries

npm run upgrade prints these steps for you. No D1 migrations, no new secrets, and the renderer version stays at 1 — nothing to re-render, and rolling back means redeploying the previous tag.

Security

npm audit goes from 6 vulnerabilities (5 high, 1 moderate) to 0. The high-severity sharp/miniflare/postcss chain entered through the old vitest-pool-workers and wrangler; the three moderate advisories were in hono and are cleared by 4.12.32. Only hono, marked, and wrangler's bundler affect the deployed Worker — the rest are build-time only.

Dependencies

  • hono 4.12.25 → 4.12.32, marked 18.0.4 → 18.0.7
  • wrangler 4.106 → 4.115, typescript 5.9.3 → 7.0.2 (the native port; zero source or tsconfig changes)
  • @cloudflare/workers-types v4 → v5 (v4 froze upstream), vitest-pool-workers 0.16 → 0.19, @types/node → 24 to match the engine floor, vitest 4.1.7 → 4.1.10, tsx 4.22.3 → 4.23.1

CI

Every workflow had been dark. All three gated their jobs on github.event.repository.private == false, which became a permanent no-op once the repo went public, and agents-docs-sync.yml additionally had no workflow_dispatch, so its gate was unreachable by any route. Guards removed, dispatch added, checkout pinned to @v6.

Documentation

Fifteen corrections from a full read pass against source. The ones that change what an operator can actually do:

  • All five OAuth providers are now documented. AGENTS.md, AGENTS-OPERATE.md, and INSTALL.md all claimed GitHub + Google only, and the AGENTS-OPERATE secret table omitted FACEBOOK_*, TWITTER_*, and DISCORD_* entirely — those providers have shipped since v1.13.0 but were undiscoverable to anyone following the guide. Setup walkthroughs, scopes, and callback URLs added for each; note the X provider slug is twitter, since it's the stored value in users.provider.
  • /admin/settings is editable, not read-only. It has persisted feature flags and display/pagination numbers to the settings D1 table since v1.10.0 — no redeploy needed. The docs still sent you to wrangler secret put for everything.
  • The comment-tree cache is the Cache API, not KV (docs/troubleshooting.md). It moved after a KV write-cap incident. The 60s TTL and the absence of global invalidation are now documented, so the up-to-a-minute delay before a moderation action reaches anonymous viewers is expected behavior rather than a bug report.
  • comment.reported — the sixth webhook event — was missing from docs/webhooks.md.
  • TURNSTILE_SECRET_KEYTURNSTILE_SECRET in examples/README.md (the wrong name simply doesn't work), and IP_HASH_SECRET is HMAC-SHA-256, not BLAKE3.

Contributor-facing: CLAUDE.md and CONTRIBUTING.md claimed tests run against Miniflare — they run in Vitest's plain node pool against hand-rolled in-memory D1/KV stubs. The README's widget size is now the measured 11.53 KB gzipped against the CI-enforced 20 KB ceiling.

Full Changelog: v1.19.1...v1.20.0

v1.19.1

Choose a tag to compare

@github-actions github-actions released this 03 Jul 21:23

Full Changelog: v1.19.0...v1.19.1

v1.19.0

Choose a tag to compare

@github-actions github-actions released this 02 Jul 13:12

Editing a comment now shows the original text instead of a blank box.

feat

  • New GET /api/v1/comments/:id/source endpoint returns a comment's raw markdown to its author, gated exactly like the edit PATCH (author-only, within the edit window).

fix

  • The widget's edit form prefills with the original markdown instead of opening empty, so authors no longer retype from scratch. Save stays disabled until the source loads.

Notes

  • No new env vars — reuses the existing EDIT_WINDOW_MINUTES.
  • No database migration. No breaking changes; npm run upgrade is a straight pull.

Full changelog: v1.18.0...v1.19.0

v1.18.0 — Telegram operator integration

Choose a tag to compare

@KingPin KingPin released this 01 Jul 04:48

Telegram operator integration — get pinged, moderate, and query Garrul from Telegram.

Highlights

  • Telegram outbound adapter — pick telegram in /admin/webhooks and store a chat id. Notifications arrive as messages with inline moderation buttons, reusing the existing per-event filter + retry queue.
  • Interactive moderation — approve / spam / restore / delete / ban author / resolve reports straight from a button tap, through the same audited path as the dashboard (actions are attributed to your linked operator id). Buttons reflect the comment's current status, not the event, so flagging spam sends back a fresh "Not spam" message and vice-versa.
  • Slash commands (read-only) — /queue, /stats, /comment <id>, /user <id>.
  • Optional daily digest — a once-daily operator summary on the existing */15 cron.

Notes for upgraders

  • Off by default. Nothing changes unless you set TELEGRAM_BOT_TOKEN. Inbound buttons/commands additionally need TELEGRAM_WEBHOOK_SECRET + a one-time setWebhook call. TELEGRAM_BOT_USERNAME (plain var) enables the one-tap link on /admin/telegram.
  • New migration 0014_telegram.sql (telegram_links) — additive. Run npm run migrate (or -- --remote for prod).
  • No breaking changes. Existing generic/Slack/Discord webhooks are unaffected.
  • Setup guide: docs/telegram.md.

What's Changed

  • feat: Telegram operator integration (notifications, moderation, queries, digest) by @KingPin in #39

Full Changelog: v1.17.0...v1.18.0

v1.17.0

Choose a tag to compare

@github-actions github-actions released this 25 Jun 03:39
c3a5adf

Moderation, thread lifecycle & community tools. All new behavior is optional and defaults off.

Highlights

  • Reader reporting — readers can flag a comment from the widget (anon-allowed, no Turnstile). New admin Reported queue tab with an open-count badge and one-click Dismiss. Fires a new comment.reported webhook.
  • Per-post close + auto-close — freeze a thread from the queue, or auto-close by instance sunset date or comment age. Closing blocks new comments/replies only; existing comments, reactions and votes stay live. Evaluated lazily at read/write — no cron.
  • One-click ban-author — ban a comment's author from the queue (admin-only; records the originating comment in audit meta, with a CGNAT caveat for anon authors).
  • Comment draft autosave — client-side localStorage, restored into an empty field, cleared on submit/cancel.
  • Community auto-collapse — reversible, client-side fold of low-scored comments once a vote floor is met. Purely cosmetic; content stays in the payload.

Operator notes

  • Migration required: run npm run migrate. 0013 adds posts.closed, posts.published_at and the reports table. Forward-only, no renderer bump.
  • New optional env vars (default off): AUTO_CLOSE_DAYS, AUTO_CLOSE_AT, COMMUNITY_MIN_VOTES, COMMUNITY_COLLAPSE_RATIO — overridable at runtime from admin Settings. See wrangler.example.toml + AGENTS-OPERATE.md §5.
  • New host attribute data-published anchors age-based auto-close (AGENTS.md).
  • New webhook event comment.reported — selectable per-endpoint in admin Webhooks; all-events endpoints receive it automatically.

Full Changelog: v1.16.1...v1.17.0

v1.16.1

Choose a tag to compare

@github-actions github-actions released this 24 Jun 03:40

Patch release — widget anti-spam fix.

Fixes

  • Reply-form honeypot is hidden again. The offscreen-hiding CSS was scoped to the top-level form only, so the reply form's hidden website honeypot rendered as a visible empty input below the reply box. Besides being a stray field, it exposed the anti-spam trap to bots (they could see which field to leave blank). The rule now applies to every form.

Upgrade notes

  • No env var, config, or migration changes.
  • Redeploy the Worker (npm run deploy) to ship the rebuilt embed.js; the fix is in the embed bundle, so embeds pick it up on next load once deployed.

Full Changelog: v1.16.0...v1.16.1

v1.16.0

Choose a tag to compare

@github-actions github-actions released this 22 Jun 21:07

Richer, actionable webhook notifications for Slack & Discord.

Highlights

  • Discord embeds. Comment notifications are now rich embeds — commenter (with avatar when available), the post title linked to the page, an event-colored accent (new = blurple, approved = green, spam/deleted = red, edited = grey), and a Links field.
  • Clickable links on both adapters. Discord and Slack notifications now carry 🔍 Open in admin (jump to the moderation detail page) and 🌐 View page.

Notes for operators

  • Admin links are built from PUBLIC_BASE_URL (already a documented var — no new config required). If it's unset, the admin link is simply omitted and the notification still sends; posts with no/invalid URL omit the page link.
  • Navigation only — no moderation-from-Discord, by design.
  • Security: comment-body injection is neutralized server-side — masked links ([x](url)) are escaped, @everyone/role mentions are defused, and every clickable URL is server-generated or scheme-validated to http(s) only.

No migrations, no new secrets. Drop-in upgrade.

Full Changelog: v1.15.0...v1.16.0