I believe in the security that transparency brings, so I’m sharing the entire technical architecture completely here, to facilitate the development of new features and the discovery of existing issues. If you also want to build a site like this, you can use it as a reference.
You can see it on the page 功能更新 CHANGE LOG; we have added many features on top of Discourse. Below, we will detail every single thing:
Single Source Site: Discourse
On a secure and stable server:
pacman -S docker git
systemctl enable --now docker
cd /var/
git clone https://github.com/discourse/discourse_docker.git discourse
cd discourse
./launcher rebuild app
Multiple Reverse Proxy Servers
The source site is on a highly reliable machine, but the network is not optimal for China, so we set up reverse proxies, using multiple high-quality return-to-China lines, but with regular machines for acceleration:
https://xjtu.app/t/topic/4330/6
Configuration File
/etc/systemd/system/caddy.service
[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target
[Service]
User=caddy
Group=caddy
Environment="ASSUME_NO_MOVING_GC_UNSAFE_RISK_IT_WITH=go1.20"
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
/etc/caddy/Caddyfile.normal
{
#auto_https off
}
xjtu.app:443, xjtu.app:443, xjtu.love:443 {
import /etc/caddy/hsts-xjtu
@default {
not path /xjtumen-custom-api/* /xjtumen-g/* /xjtumen-res/* /ip
}
reverse_proxy @default unix//var/discourse/shared/standalone/nginx.http.sock
reverse_proxy /xjtumen-custom-api/* 127.0.0.1:7010
# reverse_proxy /xjtumen-res/* https://res.xjtu.app
# handle /xjtumen-res/* {
# uri strip_prefix /xjtumen-res
# reverse_proxy /* https://res.xjtu.app
# }
import pass-ip-to-backend
route /ip {
rate_limit {remote.ip} 10r/m
}
redir /xjtumen-g /xjtumen-g/ 308
route /xjtumen-g/ {
rate_limit {remote.ip} 60r/m
}
handle /xjtumen-g/* {
uri strip_prefix /xjtumen-g
root /xjtumen-g/* /var/g/
file_server {
browse
hide .git
}
}
log {
output file /var/log/caddy/xjtu
}
}
import /etc/caddy/common
/etc/caddy/Caddyfile.rebuild
xjtu.app:443, xjtu.app:443, xjtu.love:443 {
import /etc/caddy/hsts-xjtu
respond "Jiao Da Gate is being updated, expected to complete within 10 minutes. Thanks for your patience!"
#root * /usr/share/nginx/xjtu-men-maintainence/
#root * /var/www/xjtu.app
#file_server
#rewrite / index.html
log {
output file /var/log/caddy/xjtu-rebuild
}
}
import /etc/caddy/common
/etc/caddy/common
www.xjtu.app:443, www.xjtu.app:443, www.xjtu.love:443 {
import /etc/caddy/hsts-xjtu
redir https://xjtu.app{uri} 301
}
/etc/caddy/hsts-xjtu
tls /etc/caddy/ssl/xjtu.app.cer /etc/caddy/ssl/xjtu.app.key
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Xss-Protection "1; mode=block"
X-Content-Type-Options "nosniff"
Content-Security-Policy "upgrade-insecure-requests"
Referrer-Policy "strict-origin-when-cross-origin"
Cache-Control "public, max-age=15, must-revalidate"
Permissions-Policy interest-cohort=()
Feature-Policy "accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'self'; camera 'none'; encrypted-media 'none'; fullscreen 'self'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none'; picture-in-picture *; speaker 'none'; sync-xhr 'none'; usb 'none'; vr 'none'"
}
/etc/caddy/pass-ip-to-backend
# https://caddyserver.com/docs/modules/http
reverse_proxy /ip 127.0.0.1:7001 {
header_up X-Real-IP {http.request.header.CF-Connecting-IP}
header_up X-Forwarded-For {http.request.header.CF-Connecting-IP}
header_up REMOTE_HOST {http.request.remote.host}
header_up REQ_HOST {http.request.host}
header_up REQ_HOSTPORT {http.request.hostport}
header_up REMOTE_PORT {http.request.remote.port}
header_up DURATION_MS {http.request.duration_ms}
header_up UUID {http.request.uuid}
header_up SCEME {http.request.scheme}
header_up TLS_VER {http.request.tls.version}
header_up TLS_CIPHER {http.request.tls.cipher_suite}
header_up TLS_PROTO {http.request.tls.proto}
header_up PROTO {http.request.proto}
header_up TLS_RESUMED {http.request.tls.resumed}
header_up TLS_SERVER_NAME {http.request.tls.server_name}
header_up REQ_URI {http.request.uri}
header_up REQ_METHOD {http.request.method}
header_up REQ_ORIG_METHOD {http.request.orig_method}
header_up RES_CACHE_CONTROL {http.response.header.cache-control}
header_up RES_CONTENT_LEN {http.response.header.content-length}
header_up RES_CONTENT_TYPE {http.response.header.content-type}
header_up RES_DATE http.response.header.date}
header_up RES_CSP {http.response.header.content-security-policy}
}
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
~/go/bin/xcaddy build --with github.com/xjtu-men/caddy-ratelimit@latest --with github.com/mholt/caddy-l4 --with github.com/quic-go/quic-go@v0.37.4
systemctl enable --now caddy
Plugins
Some plugins are in private GitHub repositories, for example the discourse-multiple-hostnames plugin:
git clone https://github_pat_
```<redacted>@github.com/xjtu-men/discourse-multiple-hostnames.git
hooks:
after_code:
- exec:
cd: $home/plugins
cmd:
- git clone https://github.com/discourse/docker_manager
- git clone https://github.com/discourse/discourse-follow
- git clone https://github.com/discourse/discourse-apple-add-to-homescreen
- git clone https://github.com/discourse/header-category-dropdown
- git clone https://github.com/discourse/discourse-post-voting
- git clone https://github.com/discourse/discourse-solved
- git clone https://github.com/discourse/discourse-data-explorer
- git clone https://github.com/discourse/discourse-whos-online
- git clone https://github.com/discourse/discourse-calendar
- git clone https://github.com/discourse/discourse-math
- git clone https://github.com/discourse/discourse-reactions
- git clone https://github.com/ShuiyuanSJTU/retort
- git clone https://github.com/discourse/discourse-ai
- git clone https://github.com/discourse/discourse-perspective-api
- git clone https://github.com/discourse/discourse-github
- git clone https://github.com/discourse/discourse-user-notes
- git clone https://github.com/discourse/discourse-templates
- git clone https://github.com/discourse/discourse-assign
- git clone https://github.com/discourse/discourse-cakeday
- git clone https://github.com/discourse/discourse-yearly-review
- git clone https://github.com/discourse/discourse-graphviz
- git clone https://github.com/discourse/discourse-bbcode
- git clone https://github.com/discourse/discourse-saved-searches
- git clone https://github.com/discourse/discourse-chart
- git clone https://github.com/communiteq/discourse-multiple-hostnames
- git clone https://github.com/mondiscourse/discourse-formatting-toolbar
- git clone https://github.com/paviliondev/discourse-topic-previews-sidecar
- git clone https://github.com/merefield/discourse-chatbot
- git clone https://github.com/discourse/discourse-gamification
- git clone https://github.com/discourse/discourse-policy
- git clone https://github.com/discourse/discourse-animated-avatars
- git clone https://github.com/discourse/discourse-signatures
- git clone https://github.com/paviliondev/discourse-locations
- git clone https://github.com/xjtumen/discourse-shadowban
- git clone https://github.com/xjtumen/discourse-ip-location
- git clone https://github.com/discourse/discourse-steam-login
- git clone https://github.com/discourse/discourse-geoblocking
- git clone https://github.com/communiteq/discourse-geo-blocking
- git clone https://github.com/discourse/discourse-bcc
- git clone https://github.com/discourse/discourse-shared-edits
- git clone https://github.com/paviliondev/discourse-custom-wizard
- git clone https://github.com/paviliondev/discourse-journal
- git clone https://github.com/discourse/discourse-oauth2-basic
- git clone https://github.com/discourse/discourse-openid-connect
- git clone https://github.com/discourse/discourse-microsoft-auth
- git clone https://github.com/discourse/discourse-hCaptcha
- git clone https://github.com/discourse/discourse-user-card-badges
- git clone https://github.com/communiteq/discourse-private-topics
- git clone https://github.com/paviliondev/discourse-ratings
- git clone https://github.com/discourse/discourse-surveys
- git clone https://github.com/discourse/discourse-adplugin
- git clone https://github.com/discourse/discourse-subscriptions
- git clone https://github.com/discourse/discourse-akismet
- git clone https://github.com/discourse/discourse-custom-topic-lists
- git clone https://github.com/xjtumen/discourse-group-membership-ip-block
- git clone https://github.com/discourse/discourse-chat-integration
- git clone https://github.com/davidtaylorhq/discourse-telegram-notifications
- git clone https://github.com/fokx/discourse-cjk-formatter
- git clone https://github.com/Lemon-planting-light/user-autonomy-plugin
- git clone https://github.com/Lhcfl/discourse-post-folding
- git clone https://github.com/discourse/discourse-preset-topic-composer
- git clone https://github.com/xjtumen/discourse-staff-alias
- exec:
cd: $home/plugins/discourse-animated-avatars
raise_on_fail: false
cmd:
- $home/plugins/discourse-animated-avatars/scripts/install.sh
Below is the plugin list:
* `docker_manager`
Allows management from the **web interface**, including invoking Docker update commands. *Note*: It may fail, and upon failure there is a chance of corrupting the browser cache, requiring a command-line `rebuild app`. It is recommended not to use hot‑reloading.
* `discourse-follow`
Follow other users
* `discourse-apple-add-to-homescreen`
Shows an “Add to Home Screen” prompt for Apple users' browsers. The Android (PWA) prompt is integrated in Core.
* `discourse-activity-pub`
Theoretically allows federation with Mastodon, but has never succeeded.
* `header-category-dropdown`
Allows a dropdown menu in the top bar; currently not used.
* `discourse-post-voting`
Enables up/down voting on subsequent replies within a topic.
* `discourse-topic-voting`
Creates poll/survey topics, allowing multiple or single choice.
* `discourse-teambuild`
Similar to assigning tasks to community members; currently not used.
* `discourse-solved`
Marks a particular reply as “solved”, suitable for Q&A posts.
* `discourse-data-explorer`
Executes database queries from the web interface; admin‑only.
* `discourse-whos-online`
Shows current online users; admin‑only.
* `discourse-calendar`
Calendar support.
* `discourse-math`
Supports LaTeX math formulas, single‑line/multi‑line. Optional backends: MathJAX / KaTex.
* `discourse-reactions`
Respond to topics with more emojis; default only :heart:.
* `discourse-ai`
Numerous AI features.
* `discourse-perspective-api`
Uses Google Perspective API to analyze whether posts violate community guidelines.
* `discourse-github`
Integrates with GitHub, e.g., issues and pull requests.
* `discourse-user-notes`
Allows moderators to add notes on users.
* `discourse-encrypt`
End‑to‑end chat encryption.
* `discourse-automation`
Executes automation tasks; currently unused, planned to update the https://xjtu.app/t/topic/1584 with the https://xjtu.app/tag/empty tag.
* `discourse-templates`
Allows setting topic templates.
* `discourse-assign`
Allows *assigning* topics to certain people, suitable for Q&A scenarios.
* `discourse-ai-topic-summary`
Uses AI to summarize long topics.
* `discourse-user-network-vis`
Visualizes social relationships.
* `discourse-nationalflags`
National flags extension.
* `discourse-telegram-notifications`
Syncs notifications to Telegram.
* `discourse-word-cloud`
Word count statistics.
* `discourse-chatbot`
Floating AIBot on the homepage and a private chat/message AIBot.
* `discourse-layouts`
Custom page layouts.
* `discourse-locations`
Supports location features; currently no use case, not enabled.
* `discourse-topic-list-previews`
Enables Discourse's default one‑line‑per‑post UI, achieving a *masonry* (waterfall) layout similar to apps like Xiaohongshu, where column count adjusts with browser width and mobile shows a single column. However, due to few full‑screen posts and occasional slow return from topic to homepage (reasons unknown), it is only enabled in the redditish and Material Design themes (FKBPro also includes content preview). Can be set in *user preferences*.

* `discourse-multiple-hostnames`
Allows Discourse to be accessed via multiple domains (note that only **one** domain can be configured in `app.yml`, which serves as the canonical name for search engine indexing), effectively acting as a whitelist for reverse‑proxy domain names.
[quote="えしりなか,post:6, topic:4330"]
* [xjtu.app](https://xjtu.app)
San Jose, CA
High‑quality China Telecom CN2 GIA; China Telecom, Unicom, Mobile, and Education Network are currently the best international routes. IPv4 only.
* [xjtu.love](https://xjtu.app/) domain:
San Francisco, CA
Standard route [AS4134 China Telecom Backbone](https://bgp.he.net/AS4134)
Arelion / Telia / twelve99 dynamic BGP routing
Usually usable, or as a backup when .men, .live are down. Dual‑stack IPv4/IPv6.
Refer to the image below:
[/quote]## Theme (Component)
<a name="theme"></a>
### Theme
Different themes not only have different colors and interfaces, but also enable different Theme Components. If you don’t like a certain feature, you can try another theme. You can also choose different themes on different devices.
* DiscourseDefault
The default and recommended theme; the topic list is single‑column.
**No** topic content preview.
**Yes** a sidebar showing extra information such as topic categories, popular tags, common links, etc. Material Design also has this sidebar. If you don’t like it you can switch to another theme. On mobile devices there is no sidebar.
* FKB Pro Theme
Uses a Win11‑like rounded design, **has** topic image‑text preview. **No** sidebar.
* graceful
Also has soft rounded corners, but not recommended for mobile because the edge gaps are relatively large. **No** sidebar.
* Material Design Theme
Material Design style; on mobile it’s hardly noticeable, **has** topic image‑text preview, uses a brick/window style post list that automatically adjusts column count based on page zoom, **has** a sidebar.
* redditish
Reddit style, **has** topic image‑text preview. Sidebar **only** shows recent topics.
### Theme Component
### Removed TC
### Disabled TC
https://github.com/xjtumen/discourse-custom-header-links.git
### All TC
* Anonymous Post
Works with plugins to enable posting/replying without logging in.
* Colorful categories
Changes interface colors based on category, without altering the top‑bar color, because high screen contrast and long color bars can be glaring.
* Custom Header Links
Insert links in the top bar, e.g., `评课社区` and `学习资料`.
* Icon Header Links
Add icon hyperlinks to the top bar.
* Dark-Light Toggle
Allows *manual* switching between light and dark themes; usually unnecessary as it can auto‑detect the browser’s theme.
* DiscoTOC
Automatically adds a table of contents without any manual steps.
* discourse-buttons
Customize button colors and shapes, e.g., rounded corners.
* Discourse Clickable Topic
Makes the entire topic area clickable, not just the title text.
* discourse-gifs
Editor support for GIPHY / Tenor GIF stickers.
* discourse-right-sidebar-blocks
Right sidebar.
* PDF previews
Embed PDFs in the page, no download or new tab needed.
* Play G Widget
Game widget.
* QR Code Inserter
Insert QR Code (quick‑response code) via editor, linking to any URL or the current page (when URL parameter is empty).
* Showcased Categories
Sidebar displays two categories.
* Table Builder
Insert tables via editor.
* Tag Cloud
Visualize all tags.
* Text Highlighter and Coloring Composer Button (BBCODE)
Editor supports BBCode and quick text highlighting.
* Tiles - Gallery Component
Optimally arrange multiple images.
* Topic Excerpts
Show text preview.
* Topic Thumbnails
Show image preview.
* Topic List Previews
Image and text preview.
* Topic List Item Click Animation
Topic click animation.
* xjtu.app custom theme-component
Custom CSS/JS, e.g., enable copying code block content, code blocks use Intel One Mono font, adjust top‑bar item order.
* Brand Header Theme Component
Adds an extra row above the current top bar, like a menu.
* Category Headers theme component
Add custom content to each category’s homepage.
* Discourse Docs Card Filter
Allows certain categories to have a documentation style.
* discourse-homepage-feature-component
Shows a featured topic on the homepage.
* discourse-category-icons
Category icons.
* discourse-tc-character-count
Character count in editor.
* Category Badge Styles
Set category badge to none to show only the icon.
* discourse-header-submenus
Show submenu above the normal header.
* discourse-gated-topics-in-category
Prompt unauthenticated users that they must log in to view content.
* [discourse-reply-template-component](https://github.com/discourse/discourse-reply-template-component)
Create topics/replies/private messages using templates.
<https://meta.discourse.org/t/reply-template/162373>
Default theme:
<https://github.com/OsamaSayegh/discourse-tab-bar-theme>
Loaded components:

### Color schemes
Users can choose many color schemes; the default is `OpenAI-Partial`. Inspired by the [OpenAI Developer Forum](https://community.openai.com/). OpenAI’s colors are very nice; we tried to mimic them by using the console to inspect `:root` in `color_definitions.css`, but can’t match exactly.
[details="OpenAI-Partial-Dark Palette"]
```json
"OpenAI-Partial-Dark": {
"primary": "ffffff",
"secondary": "262727",
"tertiary": "2ec27e",
"quaternary": "b9b4ec",
"header_background": "111111",
"header_primary": "dddddd",
"highlight": "b496ff",
"danger": "f17173",
"success": "09cf09",
"love": "fa6c8d",
"selected": "183d31",
"hover": "2d2e31"
}
[/details]
OpenAI-Partial Palette
"OpenAI-Partial": {
"primary": "202123",
"secondary": "ffffff",
"tertiary": "10a37f",
"quaternary": "715fde",
"header_background": "ffffff",
"header_primary": "333333",
"highlight": "482da8",
"danger": "ef4146",
"success": "009900",
"love": "fa6c8d",
"selected": "e6f3f3",
"hover": "f7f7f8"
}
Online configuration
The web interface can change many settings; the important ones include:
- base font and using Intel One Mono for code blocks
- Fix the order of categories in the left sidebar, adjust the order and visibility of top‑bar buttons on mobile and desktop
- Make the “remind to register” banner invisible to logged‑in users
- Limits for Chinese topic titles and content length, username length, reserved usernames and regex
- Conditions for different Trust Levels, allowed features and rate limits (including API key)
- max image size (KB), max attachment size (KB); requires
./launcher enter appto modify nginx config - AI model selection and service URL
Existing issues
- The Discourse Docker container uses
nginxas the server, listening on a Unix socket, then the hostcaddyreverse‑proxies it, causingperformance loss(Unix socket is efficient, can be ignored) and unnecessary memory usage. - nftables conflicts with Docker;
no firewall configurednow using a firewall.