Artalk 2.10.1
Overview
Artalk is a self-hosted, privacy-friendly comment system and an alternative to Disqus. You add a small script to your site's page template, and every page gets its own comment thread. Readers comment, reply, vote and format with Markdown, and you moderate everything from Artalk's admin sidebar.
This SecureAnyCloud image runs Artalk 2.10.1 on Ubuntu 24.04 LTS on Amazon EC2. It is one version of the SecureAnyCloud forums and social networks listing on AWS Marketplace: you pick it as the software version when you subscribe and launch. Artalk is a single Go program with its SQLite database, comment widget and admin sidebar built in. It serves one site or many, and imports comments from Disqus, WordPress and other systems.
What you get:
- No default account and no baked secret. The image ships with no administrator, no
app_key(the key Artalk signs every sign-in token with), no database and no TLS key. On your instance's first boot, the image generates a 64-characterapp_keyand a 24-character password for the site-wide administrator,admin. The configuration file stores only a bcrypt hash of the password. The password itself is in a root-only file. There is no install wizard. - No secret in a process environment or on a command line. Artalk reads its settings from
/etc/artalk/artalk.yml, which only root and theartalkservice can read. Nothing secret is passed to it in an environment variable or an argument. - Settings that cannot be changed from the web. The configuration file is read-only to Artalk, so the sidebar's Settings page cannot save. You change settings as root on the instance (Step 9), which keeps features such as upload commands out of reach of a stolen administrator session.
- Only your sites can embed it. Artalk answers cross-origin requests (CORS) and allows framing of its sidebar (
frame-ancestors) only for this instance and the sites you add withartalk-sites, never for*. Any other site's page that tries to post or vote with a reader's browser is refused. - An administrator plane that refuses other sites. nginx refuses any request to the administrator API (settings, sites, users, import and export) that another website's page sends from your browser, even with a valid sign-in.
- Sign-in rate limits on the real client address. Artalk asks for a captcha after three sign-ins or comments in a minute from one address. nginx answers
429to more than about ten sign-ins a minute from one address. nginx replaces any forwarded address header a client sends, so a client cannot dodge either limit by inventing addresses. - No outbound access from inside. On every boot, the kernel refuses the
artalkaccount any connection to the EC2 instance metadata service, link-local, private (RFC 1918), carrier-grade NAT and reserved addresses, your VPC's own address ranges, and all IPv6. You can allow one private address at a time, for example your SMTP relay (Step 10). Every outbound feature (email, notifications, captcha services, social sign-in) is off, with empty credentials. - HTTPS from the first boot. nginx serves Artalk on port 443 with a certificate generated on your instance's first boot, and port 80 redirects to HTTPS. Nothing answers until first boot has finished.
- Artalk listens on localhost only (
127.0.0.1:23366) and runs as its own unprivileged user with no Linux capabilities. - Built from source, with current fixes. Artalk 2.10.1 is built from its tagged release source with Go 1.26.10, with four of its Go libraries raised to their fixed releases. The sidebar and widget are upstream's own 2.10.1 build, checked file by file.
- A fully patched base. Ubuntu 24.04 LTS is patched at build time, with unattended security upgrades switched on.
- 24/7 SecureAnyCloud support
What is on the image: Artalk 2.10.1 (/opt/artalk/artalk), its configuration in /etc/artalk/artalk.yml, and its database, uploaded images and log in /var/lib/artalk/data. The image also includes nginx, the tools artalk-sites, artalk-admin-password, artalk-set-hostname and artalk-selftest, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu | Architecture: x86_64 | Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair, and a VPC subnet with internet access.
- A security group that allows TCP 22 (SSH) from your own network. It must also allow TCP 443 (Artalk) and TCP 80 (redirects to HTTPS) from your readers, which for a public website means from anywhere.
- A website whose page template you can edit, so you can embed the comment widget.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Artalk 2.10.1 on Ubuntu 24.04 as the software version, select your Region, and choose Launch through EC2.
Choose m5.large, your key pair, a subnet and a security group as described above. Artalk itself needs very little, so a smaller instance such as t3.small serves a busy blog.
To launch with the AWS CLI instead, replace the values in angle brackets:
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=artalk}]'Connect to your instance
Connect over SSH as the default user. For Artalk 2.10.1 on Ubuntu 24.04, the SSH user is ubuntu.
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
On first boot, the instance sets its host name and generates the certificate, the app_key and the administrator password. It then loads the egress backstop and starts Artalk and nginx. Confirm that first boot has finished:
systemctl is-active artalk-firstboot sac-artalk-egress artalk nginx
sudo test -f /var/lib/sac/data/artalk-firstboot.done && echo FIRSTBOOT_DONEVerify that all four units are active and that the first-boot marker is present:
active active active active FIRSTBOOT_DONEOnly SSH and nginx listen on the network. Artalk listens on localhost only. Confirm the listening ports:
sudo ss -lntpH | awk '{print $4, $6}' | sed -E 's/users:\(\("([^"]+)".*/\1/' | grep -v '127.0.0.5' | sort -u0.0.0.0:22 sshd 0.0.0.0:443 nginx 0.0.0.0:80 nginx 127.0.0.1:23366 artalk [::]:22 sshd [::]:443 nginx [::]:80 nginxGet your URL and administrator password
sudo cat /var/lib/sac/data/artalk-credentials.txtOnly root can read this file. It holds the URLs, the default site's name, and the administrator's user name, email and password:
# Artalk 2.10.1 - generated on first boot by artalk-firstboot.service
# The administrator password is unique to this instance; none was baked into the image. Keep this file secret. ARTALK_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ ARTALK_SIDEBAR_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/sidebar/ ARTALK_SITE=My Site ARTALK_ADMIN_USER=admin ARTALK_ADMIN_EMAIL=admin@artalk.local ARTALK_ADMIN_PASSWORD=<ARTALK_ADMIN_PASSWORD>
#
# Sign in to the sidebar (moderation, sites, settings, import/export) with the user name and email above and this
# password; 'admin' is a site-wide superuser. Readers comment through the widget you embed in your site; no reader
# account or social login is configured.
# Allow your site to embed the widget:
sudo artalk-sites add https://your-blog.example
# New administrator password:
sudo artalk-admin-password
# Self-test:
sudo artalk-selftest Change the served name:
sudo artalk-set-hostname <your-domain>
# Settings: edit /etc/artalk/artalk.yml as root, then
sudo systemctl restart artalk (the sidebar cannot save them).
# User guide: https://secureanycloud.com/stacks/artalk-2-10-1/The URL uses the instance's public DNS name from EC2 instance metadata. Opening https://<public-ip>/ redirects you to that name.
The image includes a self-test that runs through nginx. It checks:
- The HTTP redirect, the health and version endpoints, the sidebar and the widget script
- The content security policy
- That the administrator signs in with the password and that a wrong password is refused
- That the administrator API refuses requests with no sign-in or from another site, and accepts the administrator
- The cross-origin and unknown-host guards
- The service's privileges, the egress backstop and the metadata block
Run it:
sudo artalk-selftestSELFTEST-OK https 301 + healthz + version 2.10.1 (53d15ee) + sidebar + widget script + CSP frame-ancestors without * and no X-Frame-Options + administrator sign-in 200, wrong password 401 + admin API 403 anonymous, 200 administrator, 403 cross-site + 403 cross-origin POST + 444 unknown host + CORS only for trusted origins + artalk uid artalk, no capabilities, NoNewPrivs, listening 127.0.0.1:23366 only, egress backstop loaded + metadata 000 inside artalk and nginx (control 200)Sign in to the admin sidebar
Open ARTALK_SIDEBAR_URL in your browser. The certificate is self-signed, so your browser asks you to confirm it the first time. Step 11 replaces it.
Enter ARTALK_ADMIN_EMAIL and ARTALK_ADMIN_PASSWORD, then choose Login.
The Comment view lists every comment on every site this instance serves. Use All, Pending and Personal to filter it. Each comment has Reply, Collapse, Approved, Pin, Edit and Delete actions, and the administrator's own comments carry a verified badge.
Page lists the pages that have comments, User lists the readers, Site lists your sites, and Transfer imports and exports comments.
Artalk asks for a captcha after three sign-ins or comments in one minute from the same address. Wait a minute or answer the captcha.
Allow your website
This step makes Artalk work on your own site. The comment widget on your page calls Artalk's API from your page's origin, and Artalk answers only the origins it trusts. A new instance trusts only itself, so list prints nothing:
sudo artalk-sites listAdd each site that will embed comments as an exact origin: https:// plus the host name, and a port if it is not 443. artalk-sites writes the list into Artalk's trusted_domains (its CORS answer) and into nginx's frame-ancestors policy and request guards. It then restarts Artalk:
sudo artalk-sites add https://blog.example.com
sudo artalk-sites remove https://blog.example.comThe tool refuses a wildcard (* or https://*.example.com), an http:// origin, localhost and a URL with a path. Adding a site only in the sidebar's Site view is not enough, because nginx's guards do not see it.
The trade-off: each listed site can call the API with a reader's browser from its own pages. List only sites you control, and add each host name your blog answers on, for example both https://example.com and https://www.example.com.
Embed the widget in your site
Add the snippet below to your page template where the comments should appear. Use the host from ARTALK_URL.
site is the site name. The image creates My Site, and you can rename it or add more sites in the sidebar's Site view. pageKey identifies the page the thread belongs to.
<link href="https://comments.example.com/dist/Artalk.css" rel="stylesheet"> <script src="https://comments.example.com/dist/Artalk.js"></script> <div id="artalk"></div> <script> Artalk.init({ el: '#artalk', server: 'https://comments.example.com', site: 'My Site', pageKey: '/blog/hello-artalk/', pageTitle: 'Hello Artalk' }) </script>The Artalk program itself serves both widget files, so you do not depend on a CDN. Confirm that they load:
H=$(sudo sed -n 's#^ARTALK_URL=https://\([^/]*\)/$#\1#p' /var/lib/sac/data/artalk-credentials.txt)
curl -sk -o /dev/null -w 'Artalk.js: %{http_code} %{content_type}\n' --resolve "$H:443:127.0.0.1" "https://$H/dist/Artalk.js"
curl -sk -o /dev/null -w 'Artalk.css: %{http_code} %{content_type}\n' --resolve "$H:443:127.0.0.1" "https://$H/dist/Artalk.css"Artalk.js: 200 text/javascript Artalk.css: 200 text/cssReaders get a comment box with a Markdown editor, preview, emoticons and image attachments. Below it, the thread shows nested replies and voting. A reader identifies themselves with a nickname and an email address, which you can see but other readers cannot.
Readers' browsers refuse the self-signed certificate, so install a trusted one (Step 11) before you go live. The Artalk documentation lists every widget option.
The administrator
admin is a site-wide superuser. It moderates every site, manages sites and users, and imports and exports comments.
The password hash lives in admin_users in /etc/artalk/artalk.yml, which Artalk applies at every start. A password change in the sidebar's user editor therefore does not last. Change the password with the helper instead. It generates a new 24-character password, writes its hash into the configuration and the password into the credentials file, and restarts Artalk. Signed-in sessions stay valid until they expire:
sudo artalk-admin-password
sudo cat /var/lib/sac/data/artalk-credentials.txtThe administrator can also use Artalk's REST API, the same one the sidebar uses. POST /api/v2/user/access_token with the name, email and password returns a token. Send it as Authorization: Bearer <token>. Keep tokens out of command lines and shell history.
Settings, notifications and sign-in options
The sidebar's Settings page shows the configuration but cannot save it, because /etc/artalk/artalk.yml is read-only to Artalk. Edit the file as root, then restart Artalk:
sudo nano /etc/artalk/artalk.yml
sudo systemctl restart artalkThe image ships with these defaults (setting, shipped value, notes):
- captcha.enabled: true (image captcha). A captcha appears after action_limit (3) actions in action_reset (60) seconds from one address.
- img_upload.enabled: true, max_size: 5 (MB). Readers can attach images, which are stored in /var/lib/artalk/data/artalk-img.
- moderator.pending_default: false. Set it to true to hold every new comment for approval.
- email.enabled: false. Enables email notifications once you add your own SMTP relay.
- auth.enabled: false. Controls social and email sign-in for readers. All providers are off, with empty secrets.
First boot and the helpers manage app_key, admin_users, site_url and trusted_domains. Change them only with the tools named at the top of the file. The full annotated upstream reference is /usr/share/doc/sac-artalk/artalk.reference.yml.
To send reply notifications by email, set email.enabled: true, send_type: smtp and the sender send_addr. Then set your relay's host, port, username and password under email.smtp.
A relay on the internet works straight away. A relay on a private address in your VPC must also be allowed in the egress list (Step 10). The same applies to the notifiers, to a hosted captcha (Turnstile, reCAPTCHA, hCaptcha, Geetest) and to social sign-in providers, which need your own keys.
Outbound connections
Artalk makes outbound connections only for the features you switch on: email, notifications, hosted captchas, social sign-in and fetching a page's title. Some of those addresses come from settings and readers, so the artalk account is refused connections to:
- The instance metadata service
- Link-local, private, carrier-grade NAT, benchmarking (198.18.0.0/15), IETF protocol (192.0.0.0/24), multicast and reserved addresses
- This VPC's own IPv4 ranges, which the instance reads from instance metadata on every boot
- Every IPv6 address
If the VPC ranges cannot be read, the instance refuses every new IPv4 connection of the artalk account until it can read them. Artalk does not start without these rules. Inspect them:
sudo iptables -S SAC-ARTALK-EGRESS | grep -c -- '-j REJECT'
sudo ip6tables -S SAC-ARTALK-EGRESS | tail -n 1
sudo cat /run/sac/artalk-vpc-cidrs24 -A SAC-ARTALK-EGRESS -j REJECT --reject-with icmp6-adm-prohibited 172.31.0.0/16The count of refused IPv4 ranges includes your VPC's ranges, and the VPC range shown is your own.
To let Artalk reach a service on a private address, such as an SMTP relay or webhook receiver in your VPC, add its range to /etc/sac/artalk-egress-allow, one CIDR per line, and reload. Metadata and link-local addresses can never be allowed:
echo 10.20.30.40/32 | sudo tee -a /etc/sac/artalk-egress-allow
sudo systemctl reload sac-artalk-egressUse your own domain and certificate
- In your DNS, point
<your-domain>(an A record, for examplecomments.example.com) at the instance's public IP address. An Elastic IP keeps that address stable. - Tell the instance to use the new name. This sets the name nginx accepts, the request guards and Artalk's
site_url, and generates a new self-signed certificate for it:
sudo artalk-set-hostname <your-domain>- For a certificate your readers' browsers trust, put the certificate chain for
<your-domain>in/etc/ssl/artalk/artalk.crtand its private key in/etc/ssl/artalk/artalk.key(root-only, mode 600). Then runsudo systemctl reload nginx. Alternatively, put the instance behind an Application Load Balancer with an AWS Certificate Manager certificate that forwards to port 443 on the instance.
Artalk stores comments against the site name and the page key, so changing the comment server's name keeps every thread. Change the server in your embed code to match.
Data and backups
Everything Artalk keeps is in /var/lib/artalk/data: artalk.db (the SQLite database), artalk-img/ (images readers attach) and artalk.log. All of it is private to the artalk account. Check the ownership and permissions:
sudo ls -l /var/lib/artalk/data | awk 'NR>1{print $1, $3, $NF}'drwx------ artalk artalk-img -rw------- artalk artalk.db -rw------- artalk artalk.logTo take a consistent copy, stop Artalk briefly while you copy the data:
sudo systemctl stop artalk
sudo tar czf /root/artalk-backup-$(date +%F).tar.gz /var/lib/artalk/data /etc/artalk/artalk.yml
sudo systemctl start artalkThe sidebar's Transfer view exports all comments as an Artrans JSON file and imports one. To bring in comments from Disqus, WordPress, Typecho, Valine, Waline, Twikoo and other systems, first convert their export to Artrans with Artalk's Artransfer tool, then import it.
An EBS snapshot of the instance's volume is also a complete backup. Any backup holds app_key and the administrator's password hash, so keep it as safe as the instance.
Security checks
These checks confirm the hardening described above. First, verify that nginx and Artalk are blocked from the instance metadata service:
for u in nginx artalk; do printf '%s ' "$u"; systemctl show -p IPAddressDeny --value "$u" | tr ' ' '\n' | sort | tr '\n' ' '; echo; donenginx 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 artalk 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128Artalk runs as artalk with no Linux capabilities and cannot gain any. Verify it:
P=$(systemctl show -p MainPID --value artalk) ps -o user= -p "$P" grep -E '^(CapEff|CapBnd|CapAmb|NoNewPrivs)' /proc/"$P"/statusartalk CapEff: 0000000000000000 CapBnd: 0000000000000000 CapAmb: 0000000000000000 NoNewPrivs: 1The guards behave as follows:
- The administrator API refuses a request without a sign-in and one that another site's page sends.
- A comment from a site you have not listed is refused and gets no CORS answer.
- A request for a host name the instance does not serve gets no answer at all.
- Plain HTTP redirects to HTTPS.
H=$(sudo sed -n 's#^ARTALK_URL=https://\([^/]*\)/$#\1#p' /var/lib/sac/data/artalk-credentials.txt)
curl -sk -o /dev/null -w 'admin API without a token: %{http_code}\n' --resolve "$H:443:127.0.0.1" -H 'Sec-Fetch-Site: same-origin' "https://$H/api/v2/sites"
curl -sk -o /dev/null -w 'admin API from another site: %{http_code}\n' --resolve "$H:443:127.0.0.1" -H 'Sec-Fetch-Site: cross-site' "https://$H/api/v2/settings"
curl -sk -o /dev/null -w 'comment from an unlisted site: %{http_code}\n' --resolve "$H:443:127.0.0.1" -X POST -H 'Content-Type: application/json' -H 'Origin: https://evil.example' --data '{}' "https://$H/api/v2/comments"
curl -sk -D - -o /dev/null --resolve "$H:443:127.0.0.1" -H 'Origin: https://evil.example' "https://$H/api/v2/conf" | grep -ci '^access-control-allow-origin' | sed 's/^/CORS headers for an unlisted site: /'
curl -sk -o /dev/null -w 'unknown host name: %{http_code}\n' --resolve evil.example:443:127.0.0.1 https://evil.example/ || true
curl -s -o /dev/null -w 'http: %{http_code}\n' --resolve "$H:80:127.0.0.1" "http://$H/sidebar/"admin API without a token: 403 admin API from another site: 403 comment from an unlisted site: 403 CORS headers for an unlisted site: 0 unknown host name: 000 http: 301Only root can read the credentials, the configuration, the TLS key and the database. The artalk service user can also read the configuration and the database. Artalk's process environment carries no secret:
P=$(systemctl show -p MainPID --value artalk)
sudo stat -c '%a %U:%G %n' /var/lib/sac/data/artalk-credentials.txt /etc/artalk/artalk.yml /etc/ssl/artalk/artalk.key /var/lib/artalk /var/lib/artalk/data/artalk.db /proc/"$P"/environ600 root:root /var/lib/sac/data/artalk-credentials.txt 640 root:artalk /etc/artalk/artalk.yml 600 root:root /etc/ssl/artalk/artalk.key 700 artalk:artalk /var/lib/artalk 600 artalk:artalk /var/lib/artalk/data/artalk.db 400 artalk:artalk /proc/<pid>/environOther notes:
- Rate limits. Artalk's own limiter asks for a captcha after three sign-ins or comments a minute from one address (the administrator's signed-in requests are exempt). nginx also allows about ten sign-in requests a minute from one address, with a small burst, and answers
429beyond that. - Client addresses. nginx replaces any
X-Forwarded-FororX-Real-IPa client sends with the address it connected from. Only nginx can reach Artalk's port, so the limits, the comment record and Artalk's log always see the real address. If you put a load balancer in front, every reader appears to come from it, so keep the limits in mind or terminate TLS on the instance. - NVMe host identity. No NVMe-oF host identity ships in the image. If you install
nvme-cli, its host NQN and host ID are your instance's own. - Who can reach it. Keep port 22 open only to your own network. Ports 80 and 443 serve your readers.
- The build.
/usr/share/doc/sac-artalk/BUILD-INFO.txtrecords the release, the Go toolchain and the dependency updates;go.mod,go.sumandgo-version-m.txtnext to it list every library in the program, andui-manifest.txtthe sidebar and widget files. - Logs. nginx logs are in
/var/log/nginx(without query strings); Artalk logs to the journal (journalctl -u artalk) and to/var/lib/artalk/data/artalk.log.
Keep it up to date
Ubuntu security updates install automatically. To apply all updates now:
sudo apt-get update && sudo apt-get upgrade -yNew Artalk releases arrive as new versions of this listing, built and checked the same way. Do not run artalk upgrade. It would replace the checked program with a download that has none of this image's dependency fixes.
To move to a new version, take a backup or a Transfer export (Step 12), launch the new version, add your sites, and restore or import the data. Read the Artalk release notes before you move.
Support and licensing
Artalk is open source under the MIT licence (the Artalk authors). The licence text is in /usr/share/doc/sac-artalk/Artalk-LICENSE, and the source code is at github.com/ArtalkJS/Artalk.
The Go and web libraries it is built with are open source, mainly under the MIT, Apache 2.0 and BSD licences. go-version-m.txt in the same directory lists them. There is no per-user licence fee. The AMI price covers SecureAnyCloud's build, hardening, first-boot setup and support.
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
- Support: 24/7, with a 24-hour response SLA
The other versions on this listing are covered in the forums and social networks guide.
Deploy on AWS
Launch Artalk 2.10.1 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com