Discourse is a web platform for hosting and moderating online discussion.
The Tor forum is currently hosted free of charge by Discourse.org for the benefit of the Tor community.
- Tutorial
- How-to
- Reference
Tutorial
Enable new topics by email
Topic creation by email is the ability to create a new forum topic in a category simply by sending an email to a specific address.
This feature is enabled per-category. To enable it for a category, navigate to it, click the "wrench" icon (top right), open the Settings tab and scroll to the Email header.
Enter an email address under Custom incoming email address. The address to
use should be in the format <categoryname>+discourse@forum.torproject.org.
Per the forum's settings, only users with trust level 2 (member) or higher are allowed to post new topics by email.
Use the app
The official companion app for Discourse is DiscourseHub.
Unfortunately, it doesn't appear to be available from the F-Droid repository at the moment.
Mirror a mailing list
The instructions to set up a forum category that mirrors for a mailing list can be found here.
The address that needs to be subscribed to the mailing list is
discourse@forum.torproject.org.
How-to
Launch the Discourse Rails console
Log-in to the server's console as root and run:
cd /srv/discourse
./launcher enter app
rails c
Reset a user's two-factor auth settings
In case a user can't log-in anymore due to two-factor authentication parameters, it's possible to reset those using the Rails console.
First, load the user object by email, username or id:
user=User.find_by_email('email')
user=User.find_by_username('username')
user=User.find(id)
Then, simply run these two commands:
user.user_second_factors.destroy_all
user.security_keys.destroy_all
These instructions are copied from this post on the Discourse Meta forum.
Reset a user account password
Usually when there is a need to reset a user's password, the user can self-service through the forgotten password forum.
In case of issues with email, the password can also be reset from the Rails console:
First, load the user object by email, username or id:
user=User.find_by_email('email')
user=User.find_by_username('username')
user=User.find(id)
Then:
user.password='passwordstring'
user.save!
These instructions are copied from this post on the Discourse Meta forum.
Adding or removing plugins
The plugins installed on our Discourse instance are configured using Puppet, in
hiera/role/forum.yaml.
To add or remove a plugin, simply add/remove the repository URL to the
profile::discourse::plugins key, run the Puppet agent and rebuild the
container:
./launcher rebuild app
This process can take a few minutes, during which the forum is unavailable.
Discourse has a plugins directory here: https://www.discourse.org/plugins
Un-delete a topic
As an admin user, the list of all deleted topics may be shown by navigating to https://forum.torproject.org/latest?status=deleted
Tu un-delete a topic, open it, click the wrench icon and select
Un-delete topic.
Permanently destroy a topic
If a topic needs to be purged from Discourse, this can be accomplished using the Rails console as follows, using the numeric topic identifier:
Topic.find(topic_id).destroy
These instructions are copied from this post on the Discourse Meta forum.
Enter the Discourse container
It's possible to enter the Discourse container to look around, make modifications, and restart the Discourse daemon itself.
cd /srv/discourse
./launcher enter app
Any changes made in the container will be lost on upgrades, or when the
container is rebuilt using ./launcher rebuild app.
Within the container its possible to restart the Discourse daemon using:
sv restart unicorn
Read-only mode
It's possible to enable "read-only" mode on the forum, which will prevent any changes and will block and new topic, replies, messages, settings changes, etc.
To enable it, navigate to the Admin section, then Backups and click the
button labeled Enable read-only.
It's also possible to enable a "partial read-only" mode which is like normal "read-only" except it allows administrators to make changes. Enabling this mode must be done via the rails console:
Discourse.enable_readonly_mode(Discourse::STAFF_WRITES_ONLY_MODE_KEY)
To disable it:
Discourse.disable_readonly_mode(Discourse::STAFF_WRITES_ONLY_MODE_KEY)
The documentation for this feature is found at https://meta.discourse.org/t/partial-read-only-mode/210401/18
Access database
After entering the container, this command can be used to open a psql shell to
the discourse PostgreSQL database:
sudo -u postgres psql discourse
Mass-disable email digests
If a user's account email address stops working (eg. domain becomes unregistered), and email digests are enabled (the default) Discourse will keep attempting to send those emails forever, and the delivery of each single email will be retried dozens of times, even if the chance of delivery is zero.
To disable those emails, this code can be used in the rails console:
users=User.all.select { |u| u.email.match('example.com') }
users.each do |u|
u.user_option.email_digests = false
u.user_option.save
end
Pager playbook
Email issues
If mail is not going out or some recurring background job doesn't work, see the Sidekiq dashboard in:
https://forum.torproject.org/sidekiq/
Email failures, in particular, are retried for a while, you should be able to see those failures in:
https://forum.torproject.org/sidekiq/retries
Dashboard warns about failed email jobs
From time to time the Discourse dashboard will show a message like this:
There are 859 email jobs that failed. Check your app.yml and ensure that the mail server settings are correct. See the failed jobs in Sidekiq.
In the Sidekiq logs, all the failed job error messages contain
Recipient address rejected: Domain not found.
This is caused by some user's email domain going dark, but Discourse keeps trying to send them the daily email digest. See the Mass-disable email digests section for instructions how to disable the automatic email digests for these users.
Upgrade failure
When upgrading using the web interface, it's possible for the process to fail
with a Docker Manager: FAILED TO UPGRADE message in the logs.
The quickest way to recover from this is to rebuild the container from the command-line:
cd /srv/discourse
git pull
./launcher rebuild app
PostgreSQL upgrade not working
The upgrade script may not succeed when upgrading to a newer version of PostgreSQL, even though it reports success. In the upgrade log, this message is logged:
mv: cannot move '/shared/postgres_data' to '/shared/postgres_data_old': Device or resource busy
This is caused by a particularity in our deployment because postgres_data is
a mount point, so attempts to move the directory fails.
A patch to workaround this was submitted upstream and merged.
Disaster recovery
In case the machine is lost, it's possible to restore the forum from backups.
The first step is to install a new machine following the installation steps in
the Installation section below.
Once a blank installation is done, restore the Discourse backup directory,
/srv/discourse/shared/standalone/backups/default, from Bacula backups.
The restoration process is then:
cd /srv/discourse
./launcher enter app
discourse enable_restore
discourse restore <backupfilename>.tar.gz
exit
Once that's done, rebuild the Discourse app using:
./launcher rebuild app
Reference
Installation
Our installation is modeled after upstream's recommended procedure for deploying a single-server Docker-based instance of Discourse.
First, a new machine is required, with the following parameters:
- an 80GB SSD-backed volume for container images and user uploads
- a 20GB NVMe-backed volume for the database
Directories and mounts should be configured in the following manner:
- the SSD volume mounted on
/srv /srv/dockerbind mounted onto/var/lib/docker
When this is ready, the role::forum Puppet class may be deployed onto the
machine. This will install Discourse's Docker manager software to
/srv/discourse along with the TPO-specific container templates for the main
application (app.yml) and the mail bridge (mail-receiver.yml).
Once the catalog is applied, a few more steps are needed:
- Bootstrap and start Discourse with these commands:
cd /srv/discourse
./launcher bootstrap app
./launcher start app
-
Login to https://forum.torproject.org and create a new admin account
-
Create an API key using the instructions below
-
Run the Puppet agent on the machine to deploy the mail-receiver
API key for incoming mail
Our Discourse setup relies on Postfix to transport incoming and outgoing mail,
such as notifications. For incoming mail, Postfix submits it to a
special mail-receiver container that is used to deliver email into Discourse
via its web API. A key is needed to authenticate the daemon running inside the
container.
To create and configure the API key:
-
Login to Discourse using the administrator account
-
Navigate to https://forum.torproject.org/admin/api/keys
-
Click the
New API Keybutton -
In the Description write
Incoming mail, for User Level selectAll Usersand for Scope selectGranular -
Locate
emailundertopicsand check the box next toreceive emails -
Click
Save -
Copy the generated key, then logon to the Puppet server run this command to enter the API key into the database:
trocla set forum.torproject.org::discourse::mail_apikey plain
Upgrades
When versioned updates are available, an email is sent automatically by
Discourse to torproject-admin@torproject.org.
These upgrades must be triggered manually. In theory it would be possible to upgrade automatically, but this is discouraged by community members because it can throw up some excitement every now and again depending on what plugins you have.
To trigger an upgrade, simply navigate to the Upgrade page in the Discourse
admin section and hit Upgrade all, then Start Upgrade.
Sometimes, this button is greyed out because an upgrade for docker_manager
is available, and it must be installed before the other components are
upgraded. Click the Upgrade button next to it.
Discourse can also be upgraded via the command-line:
cd /srv/discourse
./launcher rebuild
Onion service
An onion service is configured on the machine using Puppet, listening on ports 80 and 443.
Internally, Discourse has a force_https setting which determines whether
links are generated using the http or https scheme, and affects CSP URLs.
When this is enabled, the forum does not work using the onion service because
CSP URLs in the headers sent by Discourse are generated with the https
scheme. When the parameter is disabled, the main issue is that the links in
notifications all use the http scheme.
So the most straightforward fix is simply to serve the forum via https on the
onion service, that way we can leave the force_https setting enabled, and the
CSP headers don't prevent forum pages from loading.
Another element to take into account is that Discourse forces the hostname as a security feature. This was identified as an issue specifically affecting forums hosted behind .onion services in this meta.discourse.org forum post.
While the solution suggested in that forum discussion involves patching
Discourse, another workaround was added later on in the form of the
DISCOURSE_BACKUP_HOSTNAME container config environment variable. When set to
the .onion hostname, the forum works under both hostnames without issue.
Directory structure
The purpose of the various directories under /srv/discourse is described in
the discourse_docker README.
The most important directories are:
containers: contains our Docker container setup configurationsshared: contains the logs, files and Postgresql database of the forum
Social login configuration
GitHub
To enable GitHub authentication, you will need the github_client_id and
github_client_secret codes. Please refer to the the official Configuring
GitHub login for Discourse documentation
for up to date instructions.
Follow these steps to enable GitHub authentication:
-
Visit
https://github.com/organizations/torproject/settings/applications. -
Click on "New Org OAuth App" or edit the existing "Tor Forum" app.
-
Follow the official instructions: https://meta.discourse.org/t/13745, or add the following configuration:
Application name: Tor Forum Homepage URL: https://forum.torproject.org/ Authorization callback URL: https://forum.torproject.org/auth/github/callback
-
Copy the
github_client_idandgithub_client_secretcodes and paste them into the corresponding fields for GitHub client ID and GitHub client secret in https://forum.torproject.org/admin/site_settings/category/login
Design
Docker manager
The Discourse Docker manager is installed under /srv/discourse and is
responsible for setting up the containers making up the Discourse installation.
The containers themselves are stateless, which means that they can be destroyed
and rebuilt without any data loss. All of the Discourse data is stored under
/srv/discourse/shared, including the Postgresql database.
Issues
There is no issue tracker specifically for this project, File or search for issues in the team issue tracker.
Maintainer, users, and upstream
Upstream is Discourse.org.
This service is available publicly for the benefit of the entire Tor community.
The forum hosted on TPA infrastructure and administered by the service admins which are lavamind, hiro, gus and duncan.
Monitoring and testing
Only general monitoring is in place on the instance, there is no Discourse-specific monitoring in place.
Logs and metrics
Logs for the main Discourse container (app) are located under
/srv/discourse/shared/standalone/log.
The mail-receiver container logs can be consulted with:
/srv/discourse/launcher logs mail-receiver
Note that this is strictly for incoming mail. Outgoing mail is
delivered normally through the Postfix email server, logging in
/var/log/mail.log*.
In addition, some logs are accessible via the browser at https://forum.torproject.org/logs (administrators-only).
An overview of all logging is available on this page: Where does Discourse store and show logs?
Backups
Backups containing the database and uploads are generated daily by Discourse
itself in /srv/discourse/shared/standalone/backups.
All other directories under /srv/discourse/shared/standalone are excluded from
Bacula backups configured from /etc/bacula/local-exclude.
It's possible to manually trigger Discourse to create a backup immediately by
entering the container and entering discourse backup on the command-line.
Other documentation
- https://meta.discourse.org/