26 KiB
Remark42

Remark42 is a self-hosted, lightweight and simple (yet functional) comment engine, which doesn't spy on users. It can be embedded into blogs, articles, or any other place where readers add comments.
- Social login via Google, Twitter, Facebook, Microsoft, GitHub, Yandex, Patreon and Telegram
- Login via email
- Optional anonymous access
- Multi-level nested comments with both tree and plain presentations
- Import from Disqus and WordPress
- Markdown support with friendly formatter toolbar
- Moderator can remove comments and block users
- Voting, pinning and verification system
- Sortable comments
- Images upload with drag-and-drop
- Extractor for recent comments, cross-post
- RSS for all comments and each post
- Telegram, Slack, Webhook and email notifications for Admins (get notified for each new comment)
- Email and Telegram notifications for users (get notified when someone responds to your comment)
- Export data to JSON with automatic backups
- No external databases, everything embedded in a single data file
- Fully dockerized and can be deployed in a single command
- Self-contained executable can be deployed directly to Linux, Windows and macOS
- Clean, lightweight and customizable UI with white and dark themes
- Multi-site mode from a single instance
- Integration with automatic SSL (direct and via nginx-le)
- Privacy focused
Demo site available with all authentication methods, including email auth and anonymous access.
All remark42 documentation is available by the link.
- Install
- Build from the source
- Development
- API
- Privacy
- Technical details
Install
Backend
With Docker
this is the recommended way to run Remark42
- copy provided
docker-compose.ymland customize for your needs - make sure you don't keep
ADMIN_PASSWD=something...for any non-development deployments - pull prepared images from the Docker Hub and start -
docker-compose pull && docker-compose up -d - alternatively compile from the sources -
docker-compose build && docker-compose up -d
Without Docker
- download archive for stable release
- unpack with
gunzip(Linux, macOS) or withzip(Windows) - run as
remark42.{os}-{arch} server {parameters...}, i.e.remark42.linux-amd64 server --secret=12345 --url=http://127.0.0.1:8080 - alternatively compile from the sources -
make OS=[linux|darwin|windows] ARCH=[amd64,386,arm64,arm]
Quick installation test
To verify if Remark42 has been properly installed, check a demo page at ${REMARK_URL}/web URL. Make sure to include remark site ID to ${SITE} list.
Register OAuth2 providers
Authentication handled by external providers. You should setup OAuth2 for all (or some) of them to allow users to make comments. It is not mandatory to have all of them, but at least one should be correctly configured.
Facebook Auth provider
- From https://developers.facebook.com select "My Apps"/"Add a new App"
- Set "Display Name" and "Contact email"
- Choose "Facebook Login" and then "Web"
- Set "Site URL" to your domain, e.g.:
https://remark42.mysite.com - Under "Facebook login"/"Settings" fill "Valid OAuth redirect URIs" with your callback URL constructed as domain +
/auth/facebook/callback - Select "App Review" and turn public flag on. This step may ask you to provide a link to your privacy policy
GitHub Auth provider
- Create a new "OAuth App": https://github.com/settings/developers
- Fill "Application Name" and "Homepage URL" for your site
- Under "Authorization callback URL" enter the correct URL constructed as domain +
/auth/github/callback, i.e.https://remark42.mysite.com/auth/github/callback - Take note of the Client ID and Client Secret
Google Auth provider
- Create a new project: https://console.cloud.google.com/projectcreate
- Choose the new project from the top right project dropdown (only if another project is selected)
- In the project Dashboard center pane, choose "API Manager"
- In the left Nav pane, choose "Credentials"
- In the center pane, choose "OAuth consent screen" tab. Fill in "Product name shown to users" and hit save
- In the center pane, choose "Credentials" tab
- Open the "New credentials" drop down
- Choose "OAuth client ID"
- Choose "Web application"
- Application name is freeform, choose something appropriate
- Authorized origins is your domain, e.g.:
https://remark42.mysite.com - Authorized redirect URIs is the location of OAuth2/callback constructed as domain +
/auth/google/callback, e.g.:https://remark42.mysite.com/auth/google/callback - Choose "Create"
- Take note of the Client ID and Client Secret
instructions for Google OAuth2 setup borrowed from oauth2_proxy
Microsoft Auth provider
- Register a new application using the Azure portal
- Under "Authentication/Platform configurations/Web" enter the correct URL constructed as domain +
/auth/microsoft/callback, i.e.https://example.mysite.com/auth/microsoft/callback - In "Overview" take note of the Application (client) ID
- Choose the new project from the top right project dropdown (only if another project is selected)
- Select "Certificates & secrets" and click on "+ New Client Secret"
Telegram Auth Provider
- Contact @BotFather and follow his instructions to create your own bot (call it, for example, "My site auth bot")
- Write down resulting token as
TELEGRAM_TOKENinto remark42 config, and also setAUTH_TELEGRAMtotrueto enable telegram auth for your users.
Twitter Auth provider
- Create a new Twitter application https://developer.twitter.com/en/apps
- Fill App name, Description and URL of your site
- In the field Callback URLs enter the correct URL of your callback handler, e.g. domain +
/auth/twitter/callback - Under Key and tokens take note of the Consumer API Key and Consumer API Secret key. Those will be used as
AUTH_TWITTER_CIDandAUTH_TWITTER_CSEC
Yandex Auth provider
- Create a new "OAuth App": https://oauth.yandex.com/client/new
- Fill "App name" for your site
- Under Platforms select "Web services" and enter "Callback URI #1" constructed as domain +
/auth/yandex/callback, i.e.https://remark42.mysite.com/auth/yandex/callback - Select Permissions. You need the following permissions only from the "Yandex.Passport API" section:
- Access to the user avatar
- Access to username, first name and surname, gender
- Fill out the rest of the fields if needed
- Take note of the ID and Password
For more details refer to Yandex OAuth and Yandex.Passport API documentation.
Patreon Auth provider
- Create a new Patreon client https://www.patreon.com/portal/registration/register-clients
- Fill App Name, Description
- In the field Redirect URIs enter the correct URI constructed as domain +
/auth/patreon/callback, i.e.https://example.mysite.com/auth/patreon/callback - Expand client details, take a note of the Client ID and Client Secret. Those will be used as
AUTH_PATREON_CIDandAUTH_PATREON_CSEC
Anonymous Auth provider
Optionally, anonymous access can be turned on. In this case, an extra anonymous provider will allow logins without any social login with any name satisfying 2 conditions:
- name should be at least 3 characters long
- name has to start from the letter and contains letters, numbers, underscores and spaces only
Importing comments
Remark42 supports importing comments from Disqus, WordPress, or native backup format. All imported comments have an Imported field set to true.
Initial import from Disqus
- Disqus provides an export of all comments on your site in a gzipped file. This option is available in your Moderation panel at Disqus Admin > Setup > Export. The export will be sent into a queue and then emailed to the address associated with your account once it's ready. Direct link to export will be something like
https://<siteud>.disqus.com/admin/discussions/export/. See importing-exporting for more details - Move this file to your Remark42 host within
./varand extract, i.e.gunzip <disqus-export-name>.xml.gz - Run import command -
docker exec -it remark42 import -p disqus -f /srv/var/{disqus-export-name}.xml -s {your site ID}
Initial import from WordPress
- Use that instruction to export comments to file using standard WordPress functionality
- Move this file to your Remark42 host within
./var - Run import command -
docker exec -it remark42 import -p wordpress -f /srv/var/{wordpress-export-name}.xml -s {your site ID}
Initial import from Commento
- Move exported json file to your Remark42 host within
./var - Run import command -
docker exec -it remark42 import -p commento -f /srv/var/{commento-export-name}.json -s {your site ID}
Backup and restore
Automatic backups
Remark42 by default makes daily backup files under ${BACKUP_PATH} (default ./var/backup). Backups kept up to ${MAX_BACKUP_FILES} (default 10). Each backup file contains exported and gzipped content, i.e. all comments. At any point, the user can restore such backup and revert all comments to the desired state.
Note: Restore procedure cleans the current data store and replaces all comments with comments from the backup file.
For safety and security reasons restore functionality not exposed outside of your server by default. The recommended way to restore from the backup is to use provided scripts/restore-backup.sh. It can run inside the container:
docker exec -it remark42 restore -f {backup-filename.gz} -s {your site ID}
Manual backup
In addition to automatic backups, user can make a backup manually. This command makes userbackup-{site ID}-{timestamp}.gz by default.
docker exec -it remark42 backup -s {your site ID}
Restore from backup
Restore will clean all comments first and then will process with complete import from a given file.
docker exec -it remark42 restore -f {backup file name} -s {your site ID}
Backup format
The backup file is a text file with all exported comments separated by EOL. Each backup record is a valid JSON with all key/value unmarshaled from Comment struct (see below).
Admin users
Admins/moderators should be defined in docker-compose.yml as a list of user IDs or passed in the command line.
environment:
- ADMIN_SHARED_ID=github_ef0f706a79cc24b17bbbb374cd234a691a034128,github_dae9983158e9e5e127ef2b87a411ef13c891e9e5
To get a user ID just log in and click on your username or any other user you want to promote to admins. It will expand login info and show the full user ID.
Docker parameters
Two parameters allow customizing Docker container on the system level:
APP_UID- sets UID to run Remark42 application in container (default=1001)TIME_ZONE- sets time zone of Remark42 container (default=America/Chicago)
see umputun/baseimage for more details
Example of docker-compose.yml:
version: '2'
services:
remark42:
image: umputun/remark42:latest
restart: always
container_name: "remark42"
environment:
- APP_UID=2000 # runs Remark42 app with non-default UID
- TIME_ZONE=GTC # sets container time to UTC
- REMARK_URL=https://demo.remark42.com # URL pointing to your Remark42 server
- SITE=YOUR_SITE_ID # site ID, same as used for `site_id`, see "Setup on your website"
- SECRET=abcd-123456-xyz-$%^& # secret key
- AUTH_GITHUB_CID=12345667890 # OAuth2 client ID
- AUTH_GITHUB_CSEC=abcdefg12345678 # OAuth2 client secret
volumes:
- ./var:/srv/var # persistent volume to store all Remark42 data
Setup on your website
Comments
It's the main widget that renders a list of comments.
Add this snippet to the bottom of web page:
<script>
var remark_config = {
host: "REMARK_URL", // hostname of Remark42 server, same as REMARK_URL in backend config, e.g. "https://demo.remark42.com"
site_id: 'YOUR_SITE_ID',
components: ['embed'], // optional param; which components to load. default to ["embed"]
// to load all components define components as ['embed', 'last-comments', 'counter']
// available component are:
// - 'embed': basic comments widget
// - 'last-comments': last comments widget, see `Last Comments` section below
// - 'counter': counter widget, see `Counter` section below
url: 'PAGE_URL', // optional param; if it isn't defined
// `window.location.origin + window.location.pathname` will be used
//
// Note that if you use query parameters as significant part of URL
// (the one that actually changes content on page)
// you will have to configure URL manually to keep query params, as
// `window.location.origin + window.location.pathname` doesn't contain query params and
// hash. For example, default URL for `https://example/com/example-post?id=1#hash`
// would be `https://example/com/example-post`
//
// The problem with query params is that they often contain useless params added by
// various trackers (utm params) and doesn't have defined order, so Remark42 treats differently
// all this examples:
// https://example.com/?postid=1&date=2007-02-11
// https://example.com/?date=2007-02-11&postid=1
// https://example.com/?date=2007-02-11&postid=1&utm_source=google
//
// If you deal with query parameters make sure you pass only significant part of it
// in well defined order
max_shown_comments: 10, // optional param; if it isn't defined default value (15) will be used
theme: 'dark', // optional param; if it isn't defined default value ('light') will be used
page_title: 'Moving to Remark42', // optional param; if it isn't defined `document.title` will be used
locale: 'en', // set up locale and language, if it isn't defined default value ('en') will be used
show_email_subscription: false, // optional param; by default it is `true` and you can see email subscription feature
// in interface when enable it from backend side
// if you set this param in `false` you will get notifications email notifications as admin
// but your users won't have interface for subscription
simple_view: false // optional param; overrides the parameter from the backend
// minimized UI with basic info only
};
</script>
<script>!function(e,n){for(var o=0;o<e.length;o++){var r=n.createElement("script"),c=".js",d=n.head||n.body;"noModule"in r?(r.type="module",c=".mjs"):r.async=!0,r.defer=!0,r.src=remark_config.host+"/web/"+e[o]+c,d.appendChild(r)}}(remark_config.components||["embed"],document);</script>
And then add this node in the place where you want to see Remark42 widget:
<div id="remark42"></div>
After that widget will be rendered inside this node.
If you want to set this up on a Single Page App, see appropriate doc page.
Themes
Right now Remark42 has two themes: light and dark. You can pick one using a configuration object, but there is also a possibility to switch between themes in runtime. For this purpose Remark42 adds to window object named REMARK42, which contains a function changeTheme. Just call this function and pass a name of the theme that you want to turn on:
window.REMARK42.changeTheme('light');
Locales
Right now Remark42 is translated to English (en), Belarusian (be), Brazilian Portuguese (bp), Bulgarian (bg), Chinese (zh), Finnish (fi), French (fr), German (de), Japanese (ja), Korean (ko), Polish (pl), Russian (ru), Spanish (es), Turkish (tr), Ukrainian (ua), Italian (it) and Vietnamese (vi) languages. You can pick one using configuration object.
Do you want to translate Remark42 to other locales? Please see this documentation for details.
Last comments
It's a widget that renders the list of last comments from your site.
Add this snippet to the bottom of web page, or adjust already present remark_config to have last-comments in components list:
<script>
var remark_config = {
host: "REMARK_URL", // hostname of Remark42 server, same as REMARK_URL in backend config, e.g. "https://demo.remark42.com"
site_id: 'YOUR_SITE_ID',
components: ['last-comments']
};
</script>
And then add this node in the place where you want to see last comments widget:
<div class="remark42__last-comments" data-max="50"></div>
data-max sets the max amount of comments (default: 15).
Counter
It's a widget that renders several comments for the specified page.
Add this snippet to the bottom of web page, or adjust already present remark_config to have counter in components list:
<script>
var remark_config = {
host: "REMARK_URL", // hostname of Remark42 server, same as REMARK_URL in backend config, e.g. "https://demo.remark42.com"
site_id: 'YOUR_SITE_ID',
components: ['counter']
};
</script>
And then add a node like this in the place where you want to see a number of comments:
<span class="remark42__counter" data-url="https://domain.com/path/to/article/"></span>
You can use as many nodes like this as you need to. The script will found all of them by the class remark__counter, and it will use data-url attribute to define the page with comments.
Also script can use url property from remark_config object, or window.location.origin + window.location.pathname if nothing else is defined.
Build from the source
- to build Docker container -
make docker. This command will produce containerumputun/remark42 - to build a single binary for direct execution -
make OS=<linux|windows|darwin> ARCH=<amd64|386>. This step will produce executableremark42file with everything embedded
Development
You can use a fully functional local version to develop and test both frontend and backend. It requires at least 2GB RAM or swap enabled.
To bring it up run:
# if you mainly work on backend
cp compose-dev-backend.yml compose-private.yml
# if you mainly work on frontend
cp compose-dev-frontend.yml compose-private.yml
# now, edit / debug `compose-private.yml` to your heart's content
# build and run
docker-compose -f compose-private.yml build
docker-compose -f compose-private.yml up
It starts Remark42 on 127.0.0.1:8080 and adds local OAuth2 provider "Dev". To access the UI demo page go to 127.0.0.1:8080/web. By default, you would be logged in as dev_user which is defined as admin. You can tweak any of supported parameters in corresponded yml file.
Backend Docker Compose config by default skips running frontend related tests. Frontend Docker Compose config by default skips running backend related tests and sets NODE_ENV=development for frontend build.
Backend development
To run backend locally (development mode, without Docker) you have to have the latest stable go toolchain installed.
To run backend - cd backend; go run app/main.go server --dbg --secret=12345 --url=http://127.0.0.1:8080 --admin-passwd=password --site=remark. It stars backend service with embedded bolt store on port 8080 with basic auth, allowing to authenticate and run requests directly, like this:
HTTP http://admin:password@127.0.0.1:8080/api/v1/find?site=remark&sort=-active&format=tree&url=http://127.0.0.1:8080
Frontend development
Developer guide
Frontend guide can be found here: ./frontend/README.md.
Build
You should have at least 2GB RAM or swap enabled for building.
- install Node.js 12.11 or higher
- install NPM 6.13.4
- run
npm installinside./frontend - run
npm run buildthere - result files will be saved in
./frontend/public
Note: Running npm install will set up pre-commit hooks into your git repository. It used to reformat your frontend code using prettier and lint with eslint and stylelint before every commit.
Devserver
For local development mode with Hot Reloading use npm start instead of npm run build. In this case, webpack will serve files using webpack-dev-server on localhost:9000. By visiting 127.0.0.1:9000/web you will get a page with the main comments widget communicating with a demo server backend running on https://demo.remark42.com. But you will not be able to log in with any OAuth providers due to security reasons.
You can attach to the locally running backend by providing REMARK_URL environment variable.
npx cross-env REMARK_URL=http://127.0.0.1:8080 npm start
Note: If you want to redefine env variables such as PORT on your local instance you can add .env file to ./frontend folder and rewrite variables as you wish. For such functional, we use dotenv.
The best way to start a local developer environment:
cp compose-dev-frontend.yml compose-private-frontend.yml
docker-compose -f compose-private-frontend.yml up --build
cd frontend
npm run dev
Developer build running by webpack-dev-server supports devtools for React and
Redux.
Privacy
- Remark42 is trying to be very sensitive to any private or semi-private information.
- Authentication requesting the minimal possible scope from authentication providers. All extra information returned by them is immediately dropped and not stored in any form.
- Generally, Remark42 keeps user ID, username and avatar link only. None of these fields exposed directly - ID and name hashed, avatar proxied.
- There is no tracking of any sort.
- Login mechanic uses JWT stored in a cookie (HttpOnly, secured). The second cookie (XSRF_TOKEN) is a random ID preventing CSRF.
- There is no cross-site login, i.e. user's behavior can't be analyzed across independent sites running Remark42.
- There are no third-party analytic services involved.
- User can request all information Remark42 knows about and export to gz file.
- Supported complete cleanup of all information related to user's activity.
- Cookie lifespan can be restricted to session-only.
- All potentially sensitive data stored by Remark42 hashed and encrypted.
