• Tip Jar

How To Setup an Arch Linux Server

This is a step by step tutorial for getting an Arch Linux server up and running with nginx, mariaDB, php, and some other goodies. At the end, I will show how to install several awesome web applications such as GNU Social, GNU Mediagoblin, and wordpress. If you have suggestions for improvements, please add them in the comments.
We start from the root prompt after you have installed and rebooted. As a quick note, if your provider doesn’t offer an Arch Linux image, you may be interested in vps2arch. If you want the fast version, then when logged into your server, simply do:

wget git.io/vps2arch
chmod 755 vps2arch
./vps2arch

It will upgrade you to Arch linux, make sure to read the stuff at the end, when the script finishes. Note, this will destroy any data you may have already on your server.

Securing Your Server

Ok, so you’re rebooted, and you are at the Arch root prompt. What next? First, make sure you have all the packages you need and create a nonroot user.

pacman -Syu --needed base base-devel
useradd -m -G wheel -s /bin/bash username

Don’t forget to set a password for your user, type passwd username and hit enter. You will be prompted to enter the password twice. You may also want to use the command visudo to allow users in the wheel group to use sudo. After typing visudo, type the following:

/wheel

Press down arrow once, so you are on the line that reads:

# %wheel ALL=(ALL) ALL

When you are on that line, type 02x which should get rid of the first 2 characters on the line, leaveing you with:

%wheel ALL=(ALL) ALL

If everything looks right, type the characters :wq to write the file and quit visudo. If something is wrong, and you need to start over, press escape, then type the characters :q! to close visudo without writing anything. Follow the steps above again, until you have completed them successfully.
Now, you should be able to logout of your root session and login as your user. Once in as your user, it’s time to do some security related stuff. First, let’s set up keys, so we don’t have to login with a password. If you don’t have an ssh key pair, you can create one (on your computer, not the server) by typing:

ssh-keygen

Follow the prompts to create your key. Now, it is time to upload the public key to your server. So, do the following:

ssh-copy-id you@YourServer'sIP

You can now login using your key. You should be prompted for the password to use your key. The first thing to do when you are successfully logged in is to test that you have sudo priveleges, and lock access to root logins at the same time. You can achieve this by typing:

sudo passwd -l root

Should you ever need to restore the root password you can do so with passwd -u root. You should never need to do so, but the option is available. Another thing you may want to know about is passwd -e. If you use this when making someone an account, it will cause their password to expire when they next login and they will have to create a new one. This is very useful for assigning temperary passwords.
Another good thing to do is disable login as root for ssh. To do so, edit /etc/ssh/sshd_config using sudo, and change the following line:

PermitRootLogin yes

so that it reads:

PermitRootLogin no

Uncomment the PasswordAuthentication line and change it from yes to no. Make sure you have your ~/.ssh/rsa_id file backed up somewhere private, where only you can access it. An external harddrive perhaps. Do not use cloud services to backup your private ssh key. It can and will be compromised. If you somehow lose all copies of the private key, you will not be able to login to your server.
Finally, reload the ssh daemon with the following command:

sudo systemctl restart sshd

We can also ban IP addresses that try to login and fail over and over. To do this, install the package sshguard:

sudo pacman -S sshguard

Now, configure iptables and add sshguard rules, and restart the services:

sudo cp /etc/iptables/empty.rules /etc/iptables/iptables.rules
sudo touch/etc/iptables/ip6tables.rules
sudo systemctl enable iptables
sudo systemctl start iptables
sudo iptables -N sshguard
sudo iptables -A INPUT -p tcp --dport 22 -j sshguard
sudo ip6tables -N sshguard
sudo ip6tables -A INPUT -p tcp --dport 22 -j sshguard
sudo systemctl enable sshguard
sudo systemctl start sshguard

Install Packages

To get the AUR helper program pacaur installed, we need to first make sure we have all necessary packages. To do this, enter the following:

sudo pacman -S git

Next, to make getting keys to verify signed packages easier, we need to edit ~/.gnupg/gpg.conf . At the bottom of the file, add:

keyserver-options auto-key-retrieve

Save the file, then clone the cower package and install it:

git clone https://aur.archlinux.org/cower
cd cower
makepkg -si
cd ..
rm -r cower
cower -d pacaur
cd pacaur
makepkg -si
cd ..
rm -r pacaur

And now it’s time to install packages that should give you a reasonably capable server:

pacaur -S --needed certbot certbot-nginx cronie \
libzip magic-wormhole-git mariadb \
nginx nodejs npm \
php php-apcu php-fpm \
php-gd php-intl php-mailparse \
php-mcrypt php-sqlite unzip \
zip

Let’s also create a user that will handle all your websites and services:

sudo useradd -m -G http -s /bin/bash html

configuration

It’s time to create some directories and edit some files.
Edit /etc/nginx/nginx.conf and change the user line so that it reads:

user html;

then find the section that starts with:

server {

Just above that line, add the following:

server_tokens off;
include /etc/nginx/sites-enabled/*;

To make sure everything is ok, you can run:

sudo nginx -t

Create the directories for your website configuration files:

sudo mkdir -p /etc/nginx/sites-{available,enabled}/

Next, edidt the file /etc/php/php.ini, and search for the following lines. If the line begins with a ; remove it to uncomment it. You will need to add the mailparse extension manually. The line should look like the following, if you notice something set differently in your php.ini, change it to match this:

expose_php = Off
extension=bz2.so
extension=curl.so
extension=exif.so
extension=gd.so
extension=gettext.so
extension=gmp.so
extension=intl.so
extension=iconv.so
extension=mailparse.so
extension=mcrypt.so
extension=pdo_mysql.so
extension=xmlrpc.so
extension=zip.so
file_uploads = On
post_max_size = 5G
upload_max_filesize = 5G
opcache.enable=1

Then edit php-fpm’s configuration file located at /etc/php/php-fpm.d/www.conf. Find and add a semi-colon to the following line:

;listen = /run/php-fpm/php-fpm.sock

Then insert a new line after that one, and insert the following:

listen = 127.0.0.1:9000

To get Mariadb ready for usage:

sudo mysql_install_db --user=mysql --basedir=/usr --datadir=/var/lib/mysql
sudo systemctl start mysqld
mysql_secure_installation

Follow the prompts, the defaults are fine for pretty much everything when offered.
Edit /etc/mysql/my.cnf and uncomment:

skip-networking

If it isn’t there by default, you may want to add the following after the line that begins with log-bin=:

# Remove logs after 1 week
expire_logs_days=7

this keeps your logs smaller, which cuts down on your disk getting too full, which can cause all kinds of interesting headaches. Another thing you can do to cut down on massive log build up is set systemd to vacuum the logs every week or two. To do this, we need to create a crontab for root.

sudo su - root
crontab -e

This will open a new crontab for root. Add the following to it to have the systemd logs vacuumed every week on Saturday at midnight. Remember, press i to get into insert mode:

# m h dom mon dow command
# Clean up the journal, leaving 1 week of logs
0 0 6 * * journalctl --vacuum-time=7d

Press the escape key, then type :wq and press enter to save the file. When you get the root prompt back, type exit to logout and return to your user account on your server.
Now, it is time to start and enable services:

sudo systemctl enable cronie
sudo systemctl start cronie
sudo systemctl enable mysqld
sudo systemctl restart mysqld
sudo systemctl enable nginx
sudo systemctl start nginx
sudo systemctl enable php-fpm
sudo systemctl start php-fpm

Finally, let’s create the directory where all your sites will be stored. So login to the html user with:

sudo su - html

Now that you are the html user, type the following:

mkdir sites

Type exit to log out of the html account. That’s pretty much it. You should now be ready to install web applications and/or create your own sites. The next few sections will guide you through setting up some awesome open source websites. first, I’d like to take a second to recommend setting up Folding At Home. This is an excellent use for your server when it has some extra cpu that it isn’t using. While not technically open source, it is still an excellent cause. If you need a domain name, and are planning to offer a publically available website, you can get them free at freenom.com. Free domains include .ga, .ml, and .tk.

GNU Social

To set up GNU Social, (I recommend the nightly branch), login to the html account:

sudo su - html

Create the directory for your domaine under the sites directory we set up earlier then clone GNU Social into it:

mkdir -p ~/sites/domain.ext
cd ~/sites/domain.ext
git clone https://git.gnu.io/gnu/gnu-social.git -b nightly

Logout of the html user:

exit

GNU Social requires a database. To creat it:

mysql -u root -p

When you hit enter you will be prompted for your password you created during the setup section. After successfully accessing mysql, enter the following, read through this part and the note afterward, do not copy/paste this part:

create database GnuSocial;
grant all privileges on GnuSocial.* to 'gnusocial'@'localhost' identified by 'PASSWORD';
flush privileges;
quit

Note in the commands above you can name the database and user whatever you would like. The word PASSWORD should be replaced by a secret password that will be used to access the database, not the actual word PASSWORD.
Now, it is time to set up the nginx side of things. For the sake of this tutorial, I’m going to use the fictional domain domain.ext. replace domain.ext with your domain. Edit /etc/nginx/sites-available/domain.ext and add these lines:

server {
listen 80;
server_name domain.ext www.domain.ext;
root /home/html/sites/domain.ext/gnu-social;
#return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl;
#ssl_certificate /etc/letsencrypt/live/domain.ext/fullchain.pem;
#ssl_certificate_key /etc/letsencrypt/live/domain.ext/privkey.pem;
#ssl on;
server_name domain.ext www.domain.ext;
root /home/html/sites/domain.ext/gnu-social;
index index.php;

error_log /var/log/nginx/error.log warn;

location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?p=$1 last;
break;
}
try_files $uri $uri/ index.php?$args /index.php?$args;
}

location ~ \.php {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
#fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param QUERY_STRING $query_string;
#fastcgi_intercept_errors on;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

location ~ \.git {
deny all;
}

location ~ \.ht {
deny all;
}
}

After this, to activate the site, run the following command:

sudo ln -s /etc/nginx/sites-available/domain.ext /etc/nginx/sites-enabled/

To generate the certificate for your GNU Social site:

sudo systemctl restart nginx
sudo certbot certonly --agree-tos --authenticator=webroot --email=YOU@EMAIL.EXT --text -w /home/html/sites/domain.ext/gnu-social -d domain.ext -d www.domain.ext

When this command completes successfully, edit /etc/nginx/sites-available/domain.ext and remove the first occurance of :

root /home/html/sites/domain.ext/gnu-social;

Also, uncomment the line:

return 301 https://$server_name$request_uri;

Uncomment these lines, and make sure the path is correct, replacing domain.ext with your domain:

ssl_certificate /etc/letsencrypt/live/domain.ext/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.ext/privkey.pem;
ssl on;

Save the file and restart nginx:

sudo systemctl restart nginx

To complete the configuration visit your domain, in our example, https://domain.ext.
Updating your installation of GNU Social is very easy:

sudo su - html
cd ~/sites/domain.ext/gnu-social
git pull

If you are already up to date, skip the next commands. If, however, there were some updates:

cd scripts
./stopdaemons.sh
php upgrade.php
./startdaemons.sh

You will probably want the daemons to start at boot. To do this, we can use a crontab:

crontab -e

Add the following lines:

# m h dom mon dow command
@reboot sleep 30 && /home/html/sites/domain.ext/gnu-social/scripts/startdaemons.sh

Congratulations! You are now running the latest and greatest. All that’s left is to log out of the html account:

exit

Media Goblin

Use pacaur to install the rest of the packages needed by Media Goblin:

pacaur -S --needed gobject-introspection{,-runtime} gst-libav gst-plugins-{bad,base,good,ugly} \
gst-python{,2} python2-babel python2-exiv2 \
python2-gobject2 python2-lxml python2-numpy \
python2-paste python2-pillow python2-scipy \
python2-virtualenv

To set up GNU Media Goblin, login to the html account. Note we will be using the fictional domain media.goblin for this tutorial:

sudo su - html

Create the directory for your domaine under the sites directory we set up earlier then clone Media Goblin into it:

mkdir -p ~/sites/media.goblin
cd ~/sites/media.goblin
git clone --recursive git://git.savannah.gnu.org/mediagoblin.git -b stable
cd mediagoblin
./bootstrap.sh
./configure
make
mkdir user_dev
chmod 750 user_dev
cp -av mediagoblin.ini mediagoblin_local.ini
cp -av paste.ini paste_local.ini
./bin/pip install scikits.audiolab

edit mediagoblin_local.ini and change the following lines. Add the stuff in brackets as it appears to the [plugins] section:

email_sender_address = "notice@mediagoblin.example.org"
email_debug_mode = false
allow_registration = false
[[mediagoblin.media_types.audio]]
[[mediagoblin.media_types.image]]
[[mediagoblin.media_types.video]]

Save the file then run the following command to initialize the database:

./bin/gmg dbupdate

Create an account on your Media Goblin installation by running:

./bin/gmg adduser

Follow the prompts, and repeat the process for anyone else you want to have an account. Doing things in this way, with public registration disabled will illiminate spam, and give you control over who has access to upload files to your server.
You will probably want to make at least one of your users an administrator. to do so, run:

./bin/gmg makeadmin USERNAME

Logout of the html account:

exit

Create /etc/systemd/system/mediagoblin-celeryd.service and add the following lines:

[Unit]
Description=Mediagoblin Celery daemon

[Service]
User=html
Group=http
Type=simple
WorkingDirectory=/home/html/sites/media.goblin/mediagoblin
# Create directory for PID (if needed) and set ownership
ExecStartPre=/usr/bin/mkdir -p /run/mediagoblin
ExecStartPre=/usr/bin/chown -hR html:http /run/mediagoblin
# Celery process will run as the `html` user after start.
Environment=MEDIAGOBLIN_CONFIG=/home/html/sites/media.goblin/mediagoblin/mediagoblin_local.ini \
CELERY_CONFIG_MODULE=mediagoblin.init.celery.from_celery
ExecStart=/home/html/sites/media.goblin/mediagoblin/bin/celery worker \
–logfile=/var/log/mediagoblin/celery.log \
–loglevel=INFO
PIDFile=/run/mediagoblin/mediagoblin-celeryd.pid

[Install]
WantedBy=multi-user.target

Create the file /etc/systemd/system/mediagoblin-paster.service and add these lines:

[Unit]
Description=Mediagoblin

[Service]
Type=forking
User=html
Group=http
Environment=CELERY_ALWAYS_EAGER=false
WorkingDirectory=/home/html/sites/media.goblin/mediagoblin
# Start mg-paster process as root, then switch to mediagoblin user/group
PermissionsStartOnly=true
ExecStartPre=/usr/bin/mkdir -p /run/mediagoblin
ExecStartPre=/usr/bin/chown -hR html:http /run/mediagoblin

ExecStart=/home/html/sites/media.goblin/mediagoblin/bin/paster serve \
/home/html/sites/media.goblin/mediagoblin/paste_local.ini \
–pid-file=/var/run/mediagoblin/mediagoblin.pid \
–log-file=/var/log/mediagoblin/mediagoblin.log \
–daemon \
–server-name=fcgi fcgi_host=127.0.0.1 fcgi_port=26543
ExecStop=/home/html/sites/media.goblin/mediagoblin/bin/paster serve \
–pid-file=/var/run/mediagoblin/mediagoblin.pid \
/home/html/sites/media.goblin/mediagoblin/paste_local.ini stop
PIDFile=/var/run/mediagoblin/mediagoblin.pid

[Install]
WantedBy=multi-user.target

Create the Media Goblin Nginx file. Edit /etc/nginx/sites-available/media.goblin

server {
listen 80;
root /home/html/sites/media.goblin/mediagoblin;
#return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl;
#ssl_certificate /etc/letsencrypt/live/media.goblin/fullchain.pem;
#ssl_certificate_key /etc/letsencrypt/live/media.goblin/privkey.pem;
#ssl on;
server_name media.goblin www.media.goblin;

include /etc/nginx/mime.types;

root /home/html/sites/media.goblin/;

autoindex off;
default_type application/octet-stream;
sendfile on;

# Gzip
gzip on;
gzip_min_length 1024;
gzip_buffers 4 32k;
gzip_types text/plain application/x-javascript text/javascript text/xml text/css;

# Change this to update the upload size limit for your users
client_max_body_size 8m;

add_header X-Content-Type-Options nosniff;

server_name media.goblin www.media.goblin;
access_log /var/log/nginx/media.goblin.access.log;
error_log /var/log/nginx/media.goblin.error.log;

# MediaGoblin’s stock static files: CSS, JS, etc.
location /mgoblin_static/ {
alias /home/html/sites/media.goblin/mediagoblin/mediagoblin/static/;
}

# Instance specific media:
location /mgoblin_media/ {
alias /home/html/sites/media.goblin/mediagoblin/user_dev/media/public/;
}

# Theme static files (usually symlinked in)
location /theme_static/ {
alias /home/html/sites/media.goblin/mediagoblin/user_dev/theme_static/;
}

# Plugin static files (usually symlinked in)
location /plugin_static/ {
alias /home/html/sites/media.goblin/mediagoblin/user_dev/plugin_static/;
}

location / {
fastcgi_pass 127.0.0.1:26543;
include /etc/nginx/fastcgi_params;

fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_param SCRIPT_NAME “”;
}
}

enable the site, restart Nginx, and get the site certificate:

sudo systemctl start mediagoblin-celeryd
sudo systemctl start mediagoblin-paster
sudo ln -s /etc/nginx/sites-available/media.goblin /etc/nginx/sites-enabled/media.goblin
sudo systemctl restart nginx
sudo certbot certonly --agree-tos --authenticator=webroot --email=YOU@EMAIL.EXT --text -w /home/html/sites/media.goblin/mediagoblin -d media.goblin -d www.media.goblin

When that command succeeds, we need to go back and enforce https on the server. Edit /etc/nginx/sites-available/media.goblin and uncomment the following line:

return 301 https://$server_name$request_uri;

Also delete the following line:

root /home/html/sites/media.goblin/mediagoblin;

Find the next line that reads:

root /home/html/sites/media.goblin/;

and below it uncomment the following three lines:

ssl_certificate /etc/letsencrypt/live/media.goblin/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/media.goblin/privkey.pem;
ssl on;

Save the file, then restart Nginx:

sudo systemctl restart nginx

Your site should be available by going to its domain, in our example media.goblin.
If you have trouble with audio failing to process after 100%, you may need to make a small edit to the Media Goblin code itself. It is pretty easy, just dow the following:

sudo su - html
cd ~/sites/media.goblin/mediagoblin/
sed -i 's/axis=1/axis=0/' mediagoblin/media_types/audio/audioprocessing.py
exit

Next Cloud

To set up Next Cloud we will have to install some packages.

pacaur -S --needed nextcloud

There are more packages that are optional and provide extra functionality:

pacaur -S --needed ffmpeg libreoffice php-imagick

Next Cloud requires a database. To creat it:

mysql -u root -p

When you hit enter you will be prompted for your password you created during the setup section. After successfully accessing mysql, enter the following, read through this part and the note afterward, do not copy/paste this part:

create database NextCloud;
grant all privileges on NextCloud.* to 'nextcloud'@'localhost' identified by 'PASSWORD';
flush privileges;
quit

Note in the commands above you can name the database and user whatever you would like. The word PASSWORD should be replaced by a secret password that will be used to access the database, not the actual word PASSWORD.
Now, it is time to set up the nginx side of things. For the sake of this tutorial, I’m going to use the fictitional domain next.cloud. replace next.cloud with your domain. Edit /etc/nginx/sites-available/next.cloud and add these lines:

upstream php-handler {
server 127.0.0.1:9000;
}

server {
listen 80;
server_name next.cloud www.next.cloud;
root /usr/share/webapps/nextcloud/;
# enforce https
#return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl;
server_name next.cloud www.next.cloud;

#ssl_certificate /etc/letsencrypt/live/next.cloud/fullchain.pem;
#ssl_certificate_key /etc/letsencrypt/live/next.cloud/privkey.pem;

add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options “SAMEORIGIN”;
add_header X-XSS-Protection “1; mode=block”;
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;

root /usr/share/webapps/nextcloud/;

location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}

rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json
last;

location = /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;
}

# set max upload size
client_max_body_size 512M;
fastcgi_buffers 64 4K;

gzip off;

error_page 403 /core/templates/403.php;
error_page 404 /core/templates/404.php;

location / {
rewrite ^ /index.php$uri;
}

location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
deny all;
}
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}

location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+|core/templates/40[34])\.php(?:$|/) {
include fastcgi_params;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param HTTPS on;
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
}

location ~ ^/(?:updater|ocs-provider)(?:$|/) {
try_files $uri/ =404;
index index.php;
}

location ~* \.(?:css|js)$ {
try_files $uri /index.php$uri$is_args$args;
add_header Cache-Control “public, max-age=7200”;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options “SAMEORIGIN”;
add_header X-XSS-Protection “1; mode=block”;
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
access_log off;
}

location ~* \.(?:svg|gif|png|html|ttf|woff|ico|jpg|jpeg)$ {
try_files $uri /index.php$uri$is_args$args;
access_log off;
}
}

Note I have removed some comments and adapted the file to match the example domain next.cloud. To see this document in its original form, please see the Next Cloud Nginx Examples page. Enable the site by creating a symlink:

sudo ln -s /etc/nginx/sites-available/next.cloud /etc/nginx/sites-enabled/next.cloud

To generate the certificate for your nextcloud site:

sudo systemctl restart nginx
sudo certbot certonly --agree-tos --authenticator=webroot --email=YOU@EMAIL.EXT --text -w /usr/share/webapps/nextcloud -d next.cloud -d www.next.cloud

When this command completes successfully, edit /etc/nginx/sites-available/next.cloud and remove the first occurance of :

root /usr/share/webapps/nextcloud/;

Also, uncomment the line:

return 301 https://$server_name$request_uri;

Uncomment these lines, and make sure the path is correct, replacing next.cloud with your domain:

ssl_certificate /etc/letsencrypt/live/next.cloud/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/next.cloud/privkey.pem;

Save the file and restart nginx:

sudo systemctl restart nginx

To complete the configuration visit your domain, in our example, https://next.cloud.

WordPress

To make the process of installing wordPress super easy, we are going to install a package called wp-cli. First, however, we need to edit /etc/php/php.ini uncomment and change the following line so that it looks like this:

phar.readonly = Off

Save the file, then run:

pacaur -S wp-cli

WordPress requires a database. To creat it:

mysql -u root -p

When you hit enter you will be prompted for your password you created during the setup section. After successfully accessing mysql, enter the following, read through this part and the note afterward, do not copy/paste this part:

create database wp_domain_ext;
grant all privileges on wp_domain.ext.* to 'wordPress'@'localhost' identified by 'PASSWORD';
flush privileges;
quit

Note in the commands above you can name the database and user whatever you would like. The word PASSWORD should be replaced by a secret password that will be used to access the database, not the actual word PASSWORD.
Now, login to your html account and set up the directory for your website, in our example, domain.ext. We will also install wordPress:

sudo su - html
cd sites
mkdir domain.ext
cd domain.ext
wp core download
wp core config --prompt

Follow the prompts. Usually, if you don’t know what should go in the prompt, just press enter for the default.
And, for the actual install:

wp core install --prompt

Once again, if you’re not sure, just press enter to select the default. A note for security, change the administrator name so that it is not admin.
Logout of the html account:

exit

Now, time for some nginx configuration. Edit /etc/nginx/sites-available/domain.ext:

server {
listen 80;
server_name domain.ext www.domain.ext;
root /home/html/sites/domain.ext/;
#return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl;
#ssl_certificate /etc/letsencrypt/live/domain.ext/fullchain.pem;
#ssl_certificate_key /etc/letsencrypt/live/domain.ext/privkey.pem;
#ssl on;
server_name domain.ext www.domain.ext;
root /home/html/sites/domain.ext/;
index index.php;

error_log /var/log/nginx/error.log warn;

location / {
try_files $uri $uri/ index.php?$args /index.php?$args;
}

location ~ \.php {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
#fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param QUERY_STRING $query_string;
#fastcgi_intercept_errors on;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

location ~ \.ht {
deny all;
}
}

To generate the certificate for your WordPress site:

sudo systemctl restart nginx
sudo certbot certonly --agree-tos --authenticator=webroot --email=YOU@EMAIL.EXT --text -w /home/html/sites/domain.ext/ -d domain.ext -d www.domain.ext

When this command completes successfully, edit /etc/nginx/sites-available/domain.ext and remove the first occurance of :

root /home/html/sites/domain.ext/;

Also, uncomment the line:

return 301 https://$server_name$request_uri;

Uncomment these lines, and make sure the path is correct, replacing domain.ext with your domain:

ssl_certificate /etc/letsencrypt/live/domain.ext/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.ext/privkey.pem;
ssl on;

Save the file and restart nginx:

sudo systemctl restart nginx

You can now visit your domain and log into WordPress!

Updating Certificates

The best way to keep your certificates up to date is to automate it using cron. You need to run the folowing command every two months or so:

certbot renew --force-renew -n

So, if you want to do it manually, and you have an excellent memory and won’t forget to do it, you’re done. For the rest of us, there’s crontab. You will need to add or edit it as root:

sudo su - root
crontab -e

To renew every other month:

0 0 1 */2 * certbot renew --force-renew -n && systemctl restart nginx

This is excellent, it’s set and forget, unless, that is, something goes wrong. You may not find out something went wrong, until your site doesn’t load. That can be a pain for you, and for your visitors. The solution I use, is, create a GNU Social account for the server, then make it post a message based on the results of the attempt to renew the certificates. You will need to login as the html user, so make sure you have exited the root account:

sudo su - html
cd sites/domain.ext/gnu-social/scripts
./registeruser.php -n USERNAME -w PASSWORD

Nickname must have only lowercase letters and numbers and no spaces. And, of course PASSWORD should not be password. Now that you have the user account for your server, exit the html account and edit your root crontab. The entry for renewing will be different now:

0 0 1 */2 * certbot renew --force-renew -n&&message="All certificates renewed successfully."||message="There were errors renewing certificates.";curl -s -u 'USERNAME:PASSWORD' --data-urlencode status="$message" -d source="$HOSTNAME" https://domain.ext/api/statuses/update.xml &> /dev/null ; systemctl restart nginx

And, since we have this nifty server account, we may as well make it even more useful. This, when added to your crontab, will issue a warning when your space is getting low:

# Post to GNU Social if space is 25% or less, check every 4 hours
0 */4 * * * bash -c "if [ $(df -h / | tail -n 1 | cut -d '%' -f1 | rev | cut -d ' ' -f 1 | rev) -ge 75 ]; then curl -s -u 'USERNAME:PASSWORD' --data-urlencode status='25% or less space remaining...' -d source=$HOSTNAME https://domain.ext/api/statuses/update.xml &> /dev/null;fi"

Save your crontab and exit root.

Acknowledgments

This article would not have been possible without help from the following people, thank y’all so much for all the help:

Jeremiah
Told me about ssh-copy-id, proofread for spelling errors and typos
Michael Taboada
Helped with configuration files, testing, research and proof reading

Thanks for reading. If you have suggestions for improvements, please leave them in the comments.

TTYtter Becomes Oysttyer

The CLI based Twitter program TTYtter has been forked and is now called Oysttyer. I know some of you are saying “yes, this is old news”, and it is, but I just kept right on procrastinating and that is why this took so long. now, however, the ttytterrc page you know and love has been updated and is now an oysttyerrc generation page. To create your .oysttyerrc, simply visit the new .oysttyerrc creation page. Dont forget to git clone the suite of extensions from github.
Although I rarely use Twitter any more, I know a lot of people still do, so I updated the extensions and settings generator code. Hopefully too, Oysttyer will be able to work flawlessly with GNU Social soon. I left the code in place for those type .rc files, so when that support is added, it should just work. You can follow me at https://social.stormdragon.tk/storm. If you need an account for GNU Social, why not sign up at https://2mb.social/main/register? I hope to see you soon in my timeline.

CLI Day 2016

I didn’t have much time to do anything for CLI Day this year. But, I certainly didn’t want to neglect the holiday I created, so I threw something together at the last minute.
This year being a leap year, I thought it may be of interest to show how to find out which day CLI Day falls on. CLI, or the Roman Numeral 151, is also the day the holiday is on, so most years, it would be 5/31. However, this year is a leap year, so this time it is on 5/30. So, to see which day try this command:

\date +'%j' --date="5/31"

This year, that command yields the result 152, meaning of course that CLI Day would fall on 5/30. I know it’s not much, but I’ve been struggling with getting a CLI based screen reader for my new Odroid, and CLI Day sneaked up on me. I was hoping to have compile instructions for speakup on the Odroid for this year’s CLI Day, but that just didn’t happen.
Anyway, happy CLI Day, and I can’t wait to see what everyone else comes up with, it is pretty awesome how many people participate now in the holiday I started just for fun and to push myself to learn to use the console for everything.

Mumble for Windows Users

Mumble is pretty much hands down the best voice chat software on the planet. It has awesome features, and uses the opus codec, so it doesn’t require a lot of bandwidth and yet the sound quality is very high. The problem is, Windows users who rely on a screen reader can’t use it because of some kind of keyboard conflict. So, until y’all can switch over to Linux, where this problem never existed to begin with, here’s a solution.
First, install mumble. Then, after running through the installation, run this registry file that disables some keyboard hooks that interfere with mumble. I’m not even sure why they are there to begin with, but doing this does not, so far as anyone who has tested it can tell, cause you to lose any keyboard functionality. That’s it, from now on your screen reader of choice and Mumble should live together in perfect harmony. Of course, since you’re using Windows, remember to virus scan anything you get before running it. This includes the registry file linked to above. I have the file hosted here on my server, but you can never be too careful.

Simple Orca Plugin System

Chrys has developed a plugin system for Orca. This plugin system allows you to write plugins for Orca in any language you want. Although not as powerful as a fully integrated plugin system, you can still accomplish a lot with what it can do. In fact, the included plugin manager is written in bash, and it allows you to enable and disable other plugins, as well as download and instal new plugins.
To install the simple plugin system, if you use Arch Linux or one of is derivatives, install the package simpleorcapluginsystem-git located in the AUR.
If you use another distribution, simply innstall manually by doing the following:

git clone https://github.com/chrys87/simple-orca-plugin-system.git
cd simple-orca-plugin-system
sudo cp -r SOPS /usr/share/

whether you install via package manager or manually, the final step is the same. To integrate the plugin system with Orca, run the following command:

/usr/share/SOPS/install-for-current-user.sh

Restart Orca, and the plugin system will be ready to use. By default, the simple plugin system includes the plugin manager, bound toorca+control+p, a plugin to tell you which workspace you are currently using, bound to orca+x, and a plugin to tell you what is currently in the clipboard, bound to orca+c.
Using the plugin manager, you can easily install more plugins. Just select Install New Plugins, then check the plugins you want to install and assign them a keybinding. There are several plugins available for download, and more are being added all the time.
If you need assistance, Chrys and I are usually in the #talkingarch channel on irc.netwirc.tk, also feel free to leave question in the comments, or contact me via the contact form.
Information for creating plugins is available in the docu.txt file included in the SOPS directory. Also, a file called hosting.txt is in the tools directory. If you want to host plugins yourself edit the file, placing your information in the variables, then upload the file to a directory on your server and rename it to index.php. Anything in the directory other than index.php will be listed as a plugin. If you host plugins and want them publically available, send me a link to your site so we can review the offered plugins and include the new site in the plugin manager.
If you just want to have your plugins hosted on an already available server, let me know here in the comments, or on GNU Social as @storm@social.stormdragon.tk. You can also contact me on Twitter as @stormdragon2976. Note that I do not check my Twitter all that often, so responces may be delayed.
The Simple Orca Plugin System is easy to use, and very useful. I hope you enjoy it.

Pick Your Poison

There are lots of windows managers out there, with varying degrees of accessibility with orca. The one I like most is Ratpoison. It is great because it is very Screen like. The problem is, its dialogs are not accessible. I started a project called strychnine a while back to help fix this problem. After realizing that Ratpoison’s keybindings were nothing more than macros to call ratpoison commands, I made a lot of progress with making pretty much the whole thing accessible to orca. I just replaced the built in dialogs with zenity based dialogs. So, where as contrl+t ? brought up an inaccessible list of keybindings, I just rebound it to run the help file through zenity –text-info. Of course, because so many things are modified, the help file might be more difficult to read. If this is the case, there are resources all over the internet with the list of bindings. I have changed very few of them from their defaults, with a few exceptions. Control+t w will open your web browser instead of the window list. I rebound the window list to control+t apostrophe and, instead of it being a box in which you enter the number or name you want, it brings up a list of open windows, just use the arrows to move to the one you want and press enter. I also added bindings that allow you to switch between open windows with alt+tab and shift+alt+tab. Note that these do not need to be preceeded with control+t.
Also, music player controls are built in as the following prepended with control+t:

Z
Previous Track
X
Play
C
Pause
V
Stop
B
Next Track
F11
Volume Down
F12
Volume Up

You can change the default escape key from control+t if you choose. In fact, the default option in the setup file is control+escape. To get strychnine set up, simply do the following:

git clone https://github.com/stormdragon2976/strychnine.git
cd strychnine
./setup.sh

If you want to install the default programs used by strychnine, run setup.sh with the -i or –install option. To create a .xinitrc file, needed for starting ratpoison, run setup.sh with the -x or –xinitrc option.
I use the SLiM login manager to start my X session. It is good because it sources the .xinitrc file for its instructions on what to load. Other login managers do not use it though, and so if you use one of them to manage Ratpoison, you may need to do the following:

sudo mkdir -p /usr/share/xsessions
sudo cp /etc/X11/sessions/ratpoison.desktop /usr/share/xsessions/ratpoison.desktop

For more information see https://wiki.archlinux.org/index.php/Ratpoison
Because the code for strychnine is changing rapidly, you may need to recreate the .ratpoisonrc file, (run setup.sh), frequently. For this reason, I made it so you can source the .ratpoisonrc file with control+t control+colon. So there’s no need to restart Ratpoison to apply new changes.
If you have a netbook, old laptop, or just want a lightning fast, easy to use, accessible desktop give Ratpoison with strychnine a try.

Command Line Word Tools

It is sometimes nice to have the ability to spellcheck, or find a definition for something right on the command line. There are several tools that can help you accomplish this. For spellchecking, I use a program called hunspell.

pacaur -S hunspell hunspell-en-us-large

hunspell-en-us-large comes from the aur, so if you don’t use pacaur or some other UR helper, you wil need to download and install it.
To use it, simply enter something like the following:

echo "neibor" | hunspell -d en_US-large
error: unknown encoding UTF8: using iso88591 as fallback
error: unknown encoding UTF8: using iso88591 as fallback
error: unknown encoding UTF8: using iso88591 as fallback
error: unknown encoding UTF8: using iso88591 as fallback
Hunspell 1.3.3
& neibor 1 0: neighbor

Here we see that neighbor is mispelled, but the output is a bit ugly. Plus, who want’s to type echo “blah” and pipe it through hunspell every time. Fortunately, you can write a small script, I call it spellcheck, that cuts all the extra typing out as well as cleaning up the output. Let’s see the output from the script:

spellcheck neibor
Hunspell 1.3.3
& neibor 1 0: neighbor

Much better. So, to create this script, simply add the following to ~/bin/spellcheck if you have added that to your path, or /usr/bin/spellcheck if you prefer to have it system wide.

#!/bin/bash
echo "$@" | hunspell -d en_US-large 2> /dev/null
exit 0

Save the file and remember to make it executable with chmod 755 /path/to/spellcheck
Now, you can spellcheck without seriously interupting your workflow. But, what if you come across a word and you don’t know its meaning? Never fear. There’s no need to go firing up a browser and wating for some dictionary site to load so you can wade through pages of ads just to get a one-line definition. There are two command line dictionaries I like to use. First, there is dictd:

pacman -S dictd

To use, type dict and the word you want:

dict skullduggery
1 definition found

From WordNet (r) 3.0 (2006) [wn]:

skullduggery
n 1: verbal misrepresentation intended to take advantage of you
in some way [syn: {trickery}, {hocus-pocus}, {slickness},
{hanky panky}, {jiggery-pokery}, {skulduggery},
{skullduggery}]

You may notice the result comes from something called wordnet. Which is the other dictionary program I tend to use from the CLI. It is in the UR:

pacaur -S wordnet

Usage can vary. On some distros like ubuntu, you type out the word wordnet and the word you want, whereas in arch, you type wn and the word you want. Here’s an example of usage:

wn storm -over

Overview of noun storm

The noun storm has 3 senses (first 2 from tagged texts)

1. (8) storm, violent storm -- (a violent weather condition with winds 64-72 knots (11 on the Beaufort scale) and precipitation and thunder and lightning)
2. (3) storm, tempest -- (a violent commotion or disturbance; "the storms that had characterized their relationship had died away"; "it was only a tempest in a teapot")
3. storm -- (a direct and violent assault on a stronghold)

Overview of verb storm

The verb storm has 5 senses (first 2 from tagged texts)

1. (2) ramp, rage, storm -- (behave violently, as if in state of a great anger)
2. (1) storm, force -- (take by force; "Storm the fort")
3. storm -- (rain, hail, or snow hard and be very windy, often with thunder or lightning; "If it storms, we'll need shelter")
4. storm -- (blow hard; "It was storming all night")
5. storm, surprise -- (attack by storm; attack suddenly)

Finally, you may need to solve an anagram. Or perhapse you’re one of those people who likes playing around with words. Either way, wordplay is for you. Just install it from the UR:

pacaur -S wordplay

Of course there are lots of options so you can refine what you are lookind for, but in its simplest form, wordplay looks like this:

wordplay dragon
Wordplay Version 7.22 03-20-96, 1991 by Evans A Criswell
University of Alabama in Huntsville criswell@XX.XXX.edu

Candidate word list : no
Anagram Generation : yes
Adjacent duplicates : no
Vowel-free words OK : no

Max anagram depth : 32
Maximum word length : 128
Minimum word length : 0

Word list file : "/usr/share/wordplay/words721.txt"
String to anagram : "DRAGON"

Initializing. Please wait while words are being loaded
and unnecessary words are being filtered out ...

42 words loaded (4096 byte block). Longest kept: 6 letters.

Anagrams found:
1. DANG OR
2. DRAGON
3. GRAD ON
4. GRAD NO
5. DRAG ON
6. DRAG NO
7. RAND GO
8. DARN GO
9. NAG ROD
10. RANG DO
11. RAG NOD
12. RAG DON
13. RAN GOD
14. RAN DOG

Note I Xed out the email address. I have no clue if it is still in use, but I don’t want to send anyone spam because of an article here. Armed with these powerful word tools, you should be able to do pretty much anything you need right from the CLI without bothering to open a browser. If you have any other useful tips for word related command line apps, please leave a comment. Thanks for reading.

CLI Day 2015

For CLI Day this year, I have put together a collection of one-line games. You can copy and paste these games directly into your terminal and play them, or you can put them into a file, one per file, and run them that way as well. These were a lot of fun to make, and even more fun to play. If you follow @storm@social.stormdragon.tk you have seen a few previews. Here are the games. I hope you enjoy them as much as I do. If you have any to contribute please leave them in the comments.

#Match the letter
s=99;while [ : ];do t=$(shuf -n1 -e {a..z});read -t0.$s -n1 -p " $t " p;if [ "$t" != "$p" ];then play -nV0 synth pi fade q 0 1 0.5;break;else play -nV0 synth .05 tri C8:C3 norm -7;fi;((s--));done
#guess 1 to 10
read -p"Guess from 1 to 10 " num;if [ $num -eq $(seq 10|shuf -n1) ]; then echo "Yay!";else echo "Boo!";fi
#order the numbers 1-9 type the number to reverse the string from that number to the right
c=0;n=$(shuf -i 1-9|tr -d "\n");while [ $n -ne 123456789 ]; do read -n1 -p$n i;l=${n%$i*};l=${#l};a=${n:0:$l};b=$(echo ${n:$l}|rev);n=$a$b;echo;((c++));done;echo "$n $c tries"
#spelling game with changable list
l=(champagne neighbor tortoise);clear;s="espeak -v en-us -a 200";for i in ${l[@]};do $s $i;read w;if [ "$w" = "$i" ];then $s "Got it!";else c=0;while [ $c -lt ${#i} ];do $s ${i:$c:1};((c++));done;fi;done &> /dev/null
#Simple configurable math game, change o for different types, set n min and x max numbers used.
n=0;x=10;o=+;m=0;r=0;s=5;t=0;a=-;while [ "${a^}" != "Q" ];do p="$(shuf -n1 -i$n-$x)$o$(shuf -n1 -i$n-$x)";read -t$s -p "$p " a;if [ $a -eq $(($p)) ] ; then echo "Yay!";((r++));s=$(echo "scale=2;$s-0.05" | bc);else echo "Wrong: $(($p))";((m++));fi 2> /dev/null;((t++));done;echo -e "\n$r right and $(($m-1)) missed of $(($t-1)) total."
#Pig dice game. any letter to roll, space or enter to bank.
c=0;p=0;s=0;t=$(shuf -n1 -e c p);while [ $c -lt 100 -a $p -lt 100 ];do if [ $t = c ];then a=$(shuf -n1 -i0-6);case $a in 0)c=$(($c+$s));s=0;t=p;echo "CPU banked $c.";;1)echo "CPU Pigged\!";s=0;t=p;;*)s=$(($s+$a));echo "CPU $s : $c";esac;else read -n1 -p "? " a;if [ -z $a ];then t=c;p=$(($s+$p));echo "You banked $p.";s=0;else a=$(shuf -n1 -i1-6);if [ $a -eq 1 ];then s=0;t=c;echo "You Pigged\!";else s=$(($s+$a));echo "You $s : $p";fi;fi;fi;done;echo "CPU $c : You $p"
#Shoot down the eroflcopter you hear when sound is centered. Random speeds.
h=0;n=1;s=20;while [ $n -eq 1 ];do x=9;y=0;while [ $x -gt 0 ];do espeak --stdout [[schXh | play -qV0 - norm remix v0.$x v0.$y&read -st0.$s -n1 p;if [ -n "$p" ]; then if [ $x -eq 5 -a $y -eq 4 -o $x -eq 4 -a $y -eq 5 ];then x=9;y=0;play -nqV0 synth pi fade h 0 1 1 pad 0 1 reverb overdrive riaa speed 32 repeat 2;play -nqV0 synth pi fade h 0 1 1 norm -18 pad 0 1 reverb overdrive riaa;((h++));s=$(seq 10 20|shuf -n1);else n=0;fi;fi;((x--));((y++));done;done;echo "You shot down $h eroflcopters."
#Talking CLIcat (pitch shifter).
e="norm pitch 1000";echo "Hold down control+c to exit.";f=$(mktemp -u);while :;do rec -q $f.wav silence 0 1 0:00:00.5 25% && play -qV0 $f.wav $e;done
# Small polyphonic music synthesizer.
n=CDEFGAB;l=asdfghj;while read -n1 k;do x=$(tr $l $n< <<$k);play -qn synth pl ${x}3 fade 0 .7 & >/dev/null;done

Charm Works Again

This is just a quick post to test the GNU Social Comments plugin. I decided to try charm again to post this, and was pleasantly surprised to find it works again. Something in the last wordpress updates broke it, but now it’s back.
Just as a reminder, CLI Day 2015 is approaching quickly. GNU Social users can join the CLI Day Group. Don’t have a GNU Social account? Get one at social.2mb.solutions. Let’s make this year the biggest CLI Day ever!

A Better Way to Microblog – GNU Social!

GNU Social is extremely cool. Not only can you have your own microblog, under your terms, with your rules, but you can also follow other people who have their own microblog as well. Not up to running your own? No problem, because there are plenty of public nodes out there already set up and waiting for you to make an account. I personally recommend 2mb.social or social.mayfirst.org, but those are just two of the ones I know of. There’s also Quitter.se, or gnusocial.net. All you have to do to get microblogging in a matter of a couple minutes is go sign up at one of the sites listed above. But, if you wanna take the bull by the horns, you can install GNU Social by downloading it from its git repository or from the .tar.gz file.
To follow a person, go to their page, https://social.stormdragon.tk/storm, for example and click on subscribe. Enter your webfinger (the full URI to your account) into the box, you@wherever.whatever, then click subscribe. You will be presented with a page to confirm your subscription.
You can also follow people from your own page. To do so, click on profile, remote, and enter the person’s webfinger into the box and confirm you want to subscribe to them.
As with other microblogging services, hash tags are used. In fact, you can see a list of trending tags right on the site. However there is another aspect to GNU Social not available in other microblogging options. If you prefix a word with an exclamation point, it will post to a group, so long as you are subscribed to said group. People in a group see things posted by everyone in that group regardless of following status. You may choose to post your currently playing music track to the !listening group, which gasher, (see below), does for you automatically. Here are some groups I recommend:

Alter Aeon>
Keep up with happenings on the game Alter Aeon.
Listening
Listening to something awesome? Post it here!

When you are setup and ready to rock, you may be interested in some clients for GNU Social. I am currently developing a command line client called Gasher. It is very functional so far and contains features you won’t likely find in most other clients, including the ability to post your currently playing song or the ability to record and post sound clips right from the program itself. For Arch Linux users, Gasher is available in the AUR as gasher-git.
The previous version of TTYtter also works well as a client, so long as you add a few things to your .ttytterrc before launching it. First, install the required version of TTYtter:

sudo wget -O /usr/bin http://www.floodgap.com/software/ttytter/dist2/2.0.00.txt

Then, create ~/.ttytterrc with the minimum of the following settings. Note, for the following example, I’m going to use https://social.2mb.solutions:

apibase=social.2mb.solutions/api
authtype=basic
linelength=512
user=USERNAME:PASSWORD
ssl=1
vcheck=0

You are now ready to use GNU Social with TTYtter, awesome! You may have noticed we had to set the line length in the above example. That’s because GNU Social does away with that silly 140 character limit, allowing the site administrator to set the character limit to whatever they wish. So, prepare to get used to being able to say what you want to say without running out of space.
BitlBee is another option as a GNU Social client. It’s been available since back when Identica was actually good. The third parameter in the account add line isn’t really needed, so just put whatever you want there. To add your account:

account add identica foo
account identica set base_url https://social.2mb.solutions/api
account identica set oauth false
account identica set message_length 512
account identica set username USER
account identica set password PASSWORD

If you would like an extra layer of protection, so you don’t accidently post something you don’t mean to, then do the following:

account identica set commands strict

With this option set, you have to type post then the text you want to post. Finally, to enable the account:

account identica on

For those of you who aren’t into the command line, there is a nice graphical app called Yaics which may or may not be in your distro’s repositories. For Arch Linux, it is available in the AUR. The source is available at https://gitlab.com/stigatle/yaics. It is accessible with orca, but you will have to use flat review almost exclusively. During configuration, if you find text boxes that don’t say what they are, just press orca+numpad5 for desktop layout, or orca+k for laptop layout, and it will tell you what the box is.
For Android users, the application Mustard is a great way to connect to your GNU Social account. Install it from the Playstore. Setup is straight forward and easy.

  • Tip Jar