Preface
Recently, while working on my own forum (bbs.eeclub.top), I needed a “reply to view” feature: members must reply to a topic before they can see hidden resource links, download addresses, and other content in the post. On the one hand, this can encourage forum activity; on the other, it helps prevent people from taking content without contributing.
Discourse itself only has a [hide] tag (which hides content from anonymous users), so it doesn’t meet the need for “reply to unlock” functionality at all. After searching online and finding no ready-made plugin, I decided to write one myself, naming it discourse-reply-to-view. The entire project was developed with assistance from AI (GLM). I handled requirements, testing, and acceptance. The backend, frontend, and tests total more than 2,000 lines of code, and the development experience was remarkably smooth.
Recommendations and introductions to various AI large-model API platforms: https://blog.zeruns.com/archives/947.html
Plugin Introduction
Discourse-reply-to-view is a Discourse content-protection plugin that provides three BBCode tags:
| Tag | Function |
|---|---|
[reply-visible]content[/reply-visible] |
Reply to view: visible only after replying in the current topic |
[login-visible]content[/login-visible] |
Login to view: visible after registering or logging in |
[reply-visible=N]content[/reply-visible] |
Reply count: visible after posting at least N replies in the current topic (optional feature) |
Markdown can be nested normally inside the tags. Code blocks, images, links, and lists are unaffected. The two tags can also be placed on a single line ([reply-visible]xxx[/reply-visible]).
Plugin Screenshots
Plugin appearance in the editor:
Appearance on article pages, with support for multiple languages:
Main Features
- Server-side permission checks: Visibility of hidden content is determined 100% on the server. The
cookedfield in the database does not store any hidden source text. Search indexes, notification emails, daily digests, Onebox previews, topic-list summaries, RSS, and the raw export API are all protected against leaks. This is not a fake frontend-only concealment using CSS. - Three unlocking modes:
any_reply(any reply unlocks everything, by default),exact_post(you must reply to the post containing the content), and count mode[reply-visible=N] - Trust-level exemptions: Users at or above a specified TL can view content without replying (disabled by default)
- Usage permission control: Restrict the use of hidden-content tags to users above a specified trust level
- Editor integration: The editor’s “+” menu includes two insertion buttons on both desktop and mobile. Clicking one automatically wraps the selected text.
- WYSIWYG for authors: When authors view their own posts, hidden content is shown with a dashed border and an info bar, making it easy to confirm the tag’s position.
- Automatic unlocking after replying: After a successful reply, the page automatically refreshes the relevant section, without requiring a manual full-page refresh.
- Protection against misplaced injection: Each hidden block includes a fingerprint check. If the values do not match during cooking and serialization, the entire post is downgraded to a placeholder. It is better to hide content than inject it in the wrong place.
- Full language support: All 49 languages supported by Discourse are included, with both frontend text and backend settings translated (including Simplified Chinese, English, Japanese, and Korean).
- Multilingual content leak protection: Sites with content localization enabled (automatic post translation) are also handled, preventing translated versions from bypassing permissions and exposing hidden content.
Usage
Insert tags through the “+” menu in the editor, or write the BBCode directly:
[reply-visible]
Write content here that can only be seen after replying, such as a download link
[/reply-visible]
[login-visible]
Write content here that can only be seen after logging in
[/login-visible]
[reply-visible=5]
Content that can only be seen after posting 5 replies (count syntax must be enabled in the admin panel)
[/reply-visible]
Frontend behavior: users who do not meet the requirements see a placeholder box (reply-to-view uses a blue theme, while login-to-view uses a green theme). Clicking the button takes them directly to the reply box or login page. Once the requirements are met, the full content is displayed automatically.
Security Design
Security is what I value most about this plugin. After all, “fake concealment” (hiding content on the frontend while leaving all the data available) is essentially useless. Its security architecture is designed as follows:
- No source text in storage: During Markdown rendering, the source text inside the tags is discarded, and the database stores only a fingerprinted placeholder container,
<div class="rtv-block">; - Injection during serialization: The source text is dynamically injected according to the current user’s permissions only when the API response is serialized. If the content is not unlocked, it cannot be retrieved—not even from the HTML source;
- All output channels blocked: Vulnerable channels such as raw exports, search indexes, email notifications, revision-history diffs, and localized translation versions are all covered;
- No cache-based leak window: Permissions are checked in real time and take effect immediately after a reply. Low-permission users cannot receive high-permission cached content.
Installation
For Discourse deployed using the official Docker setup, edit containers/app.yml and add one line under hooks → after_code:
hooks:
after_code:
- exec:
cd: $home/plugins
cmd:
- git clone https://github.com/discourse/docker_manager.git
- git clone https://github.com/zeruns/discourse-reply-to-view.git
Then rebuild the container (frontend assets must be recompiled, so you must rebuild rather than restart):
cd /var/discourse
./launcher rebuild app
After installation, the following settings can be adjusted under Admin → Settings → Plugins:
enable_rtv: Master plugin switchreply_to_view_mode: Reply-to-unlock mode (any reply / exact post)reply_to_view_allow_count: Whether to enable[reply-visible=N]count syntaxmin_trust_level_to_bypass: Trust-level exemption (0 = no exemption)min_trust_level_to_use: Minimum trust level allowed to use hidden-content tags
Compatibility
Developed and tested on the latest version of Discourse (the master branch, built in 2026). It depends on the newer markdown-it BBCode system, and Discourse 3.4 or later is recommended. Testing includes 79 RSpec cases covering the permission matrix and various leak vectors; all tests pass consistently when run in randomized order.
Related Tutorials
- Discourse forum setup tutorial—deploy an open-source Discourse community forum from scratch: https://blog.zeruns.com/archives/919.html
- Discourse AI configuration tutorial—use the SiliconFlow API to enable AI features for free: https://blog.zeruns.com/archives/917.html
Project Repository and Demo
- GitHub repository: https://github.com/zeruns/discourse-reply-to-view
- Demo forum: https://bbs.eeclub.top/
- Author’s blog: https://blog.zeruns.com/
If you find it useful, please support the project by giving it a Star on GitHub~
Conclusion
This project was also an experiment in “AI-led development with human acceptance testing”: I defined the requirements, edge cases, and security scenarios, while AI generated the code, test cases, and documentation. Along the way, it uncovered several leak vectors I hadn’t even considered myself, including the raw export API, revision-history diffs, and translated versions. AI really is more reliable than humans at enumerating edge cases of this kind. If you enjoy tinkering with forums, give it a try. If you encounter any issues, feel free to open an issue on GitHub or leave a comment.
Recommended Reading
- High-value and affordable VPS/cloud server recommendations: https://blog.zeruns.com/archives/383.html
- Minecraft server setup tutorial: https://blog.zeruns.com/tag/mc/
- Complete Hermes Agent deployment guide—step by step to building your first AI assistant: https://blog.zeruns.com/archives/939.html
- Weijian W96Pro mini fan review and teardown analysis: https://blog.zeruns.com/archives/954.html
- Ugreen DXP4800Pro NAS unboxing, review, and teardown: https://blog.zeruns.com/archives/949.html
- 【Open source】Chunzhen IP database online lookup system—IP geolocation lookup, local IP lookup, and domain resolution: https://blog.zeruns.com/archives/944.html
English Version of the Article: https://blog.zeruns.top/archives/100.html

