The Shift-CTRL Space Library project is a self-hosted personal e-Book (Calibre) library "in a box" designed to make it easy to securely share (primarily) texts among small- to medium-sized groups who are tightly resource-constrained. This means we are focused on supporting extremely low-cost, easily-available hardware. It's important to us that small groups or individuals without many resources have what they need to operate effectively despite having at best minimal support from existing economic or political systems.
That said, to set up your own Library, some initial resources are required. These include:
- An inexpensive computer, such as any model of Raspberry Pi ("RPi"). You can purchase a new RPi for as little as $35 USD. Alternatively, you can often acquire a Raspberry Pi for free by simply asking around local programmer's meetups or other tech-focused watering holes; someone much richer than you has probably bought one of these "for a weekend project" that they never got around to actually doing. ;)
- An Internet connection, at least while setting up the Library initially. The cheapest plan from your local Internet Service Provider will almost certainly suffice. If you want to make your Library accessible to people who are not physically nearby (such as connected to the same Wi-Fi network as the Library hardware itself), you will also need to retain an Internet connection so that the Library can function as a remote server. Otherwise, you can simply set up the Library in a location where you have Internet access and then move it to some place you do not; the Library will continue to make its content available to the local area network to which it is connected.
- Some basic knowledge of command-line GNU/Linux system administration. If this is a new area for you, we highly recommend the NYC chapter of the Anarcho-Tech Collective's "Foundations" series. In particular, we suggest starting at their "Securing a Shell Account on a Shared Server" guide if command-line interfaces are completely new to you.
To deploy and manage a Library, this project uses Ansible playbooks that provision a simple Web server built into Calibre (its Content server) to a given host or set of hosts.
Moreover, by default, the provision.yaml
playbook will build a Tor server from the Tor Project's GPG-signed source code, and expose the Calibre library as a stealth Onion service to a number of authenticated clients. This means people who want to access the Library from afar will need to use and configure their local Tor clients (such as Tor Browser) with the appropriate access credentials ("library cards") before they are able to connect. Additionally, you can optionally enable "onionshare_receiver_mode
" (based on OnionShare), which will create a second Onion service that allows anyone to anonymously upload new files in a sort of drop box for a Librarian to review in order to more organically grow the Library. Both of these features require a sustained Internet connection.
Once again, we encourage you to acquire the skills you need to manage this Library from the Anarcho-Tech Collective's great guides and practice labs. It won't take as long as you might fear, and what you learn will be useful for the rest of your life. Promise.
In order to simplify understanding this project and to make it easier to communicate with others who use this project, we use the metaphor of a physical community library.
- The folder that contains the e-books and other content available for browsing, sharing, and reading is called a library.
- The machine on which a library (folder) exists that is running the Calibre content server software is called a library branch.
- The people responsible for adding, removing, and cataloguing the contents of the library are called librarians. A library branch must have at least one librarian, although any number of librarians can share responsibility for a single library branch.
- People who have been pre-approved by a librarian to access the library's content even while they are not physically near the library itself are given a set of access credentials that we call library cards. These are optional; by default, no remote access is permitted without a library card.
This section describes the process of setting up a new Library branch in the default configuration.
We intend a Library branch to be a computer that is not your personal computer. The Library branch hardware is, however, configured and managed from your personal computer. Therefore, to use these playbooks, you need the following software installed on your own computer:
We call the computer you choose to manage the Library from the Ansible controller. For the initial configuration, you'll also need the ability to connect via SSH to the Library branch computers (i.e., the machines you list in your Ansible host inventory).
Once Calibre is installed, you will need to create a Calibre Library. This Library is a folder, much like the iTunes Music folder, that is managed by Calibre. Use the Calibre interface to make any changes to the Library. Eventually, Ansible will be used to synchronize the Calibre Library folder on your Ansible controller over to the Library branch computers.
The Library branch computers must have sufficient storage space available to hold the contents of the library itself. (A future version of these playbooks may offer a way to use network-attached storage instead of directly-attached storage, like an SD card, to store the library content itself.) If your Library branch computers are Raspbery Pis, a suitable 128GB SD card currently retails for about $20 USD and will be able to comfortably house approximately 10,000 e-books, depending on the specific books, of course.
In the simplest case:
- You can use NOOBS to install Raspbian onto a Raspberry Pi.
- Once the installation is complete, use Raspbian's included
raspi-config
utility to enable the SSH service, which will make it possible to remotely administer the Pi. - Connect the Raspberry Pi to a network, such as your home Wi-Fi network. (See the Raspberry Pi documentation for some examples.)
If successful, you should now be able to access the Raspberry Pi's SSH service port for remote administration from your Ansible controller. You can test this with a command such as nc -vz raspberry.local 22
, which will tell you that the connection "succeeded
" if your Ansible controller can reach your Library branch. You will then be able to use this project's provided example/hosts
inventory file to experiment with the playbooks unmodified.
After installing the prerequisite software, download this project's code. You can do so via command-line Git:
git clone https://github.com/shiftctrlspace/library.git
or using this "Download" link from the Web site.
Next, you will need to retrieve this project's dependent Ansible roles (modules). Install them with ansible-galaxy
:
cd library
ansible-galaxy install -r requirements.yaml
The next step is to make a list of the Library branches (hosts) you'd like to manage. The files in inventories/example
provide a start. You can copy this directory hierarchy to another folder (such as inventories/production
) and modify the hosts
file and any variables in the group_vars/
folder therein to customize your deployment.
The example inventory file assumes a single, almost untouched Raspbian server. You can use it immediately like this:
ansible-playbook -i inventories/example/hosts --ask-pass playbooks/main.yaml
🔰 The default administrative user on a Raspbian system is
pi
, and its password israspberry
. At a minimum, you should change this password on any production systems. We recommend changing the username as well.
Depending on the speed of your Internet connection and your hardware, the deployment could take quite a bit of time. By default, a successful deployment will expose the (empty) Calibre Library as an authenticated stealth Onion service. You can retrieve the Onion service authentication credentials to a given Library branch (such as raspberry.local
) for a given client (such as alice
) like this:
# Using Ansible:
ansible raspberry.local -i inventories/example/hosts --ask-pass --become -a "grep 'alice$' /var/lib/tor/onion-services/onion-library/hostname"
# Using plain old SSH:
ssh pi@raspberry.local "sudo grep 'alice$' /var/lib/tor/onion-services/onion-library/hostname"
Once you have the Onion service authentication cookie for some user (their "library card"), you should securely share it with them so that they may configure their local Tor to access your Library branch.
Once provisioned, each host will have a system user with which you can manage your library. The required calibre
role will generate an Ed25519 SSH key for doing so. This key will be placed in your user's $HOME/.ssh/
directory on the Ansible controller. You can then use this key to log in to the Calibre server's host machine with a command such as:
ssh -i ~/.ssh/raspberry.local/srv/calibre/.ssh/calibre_librarian_ed25519 calibre@raspberry.local
We recommend adding books to library branches by synchronizing them with a master copy of your content located on a workstation (such as your own laptop, the Ansible controller); to support multiple Librarians, a simplistic solution is to share the workstation itself or to place the Calibre Library folder on a shared fileserver. The provided example Ansible inventory expects to find this master Calibre Library on your Ansible controller in its ~/Documents/Calibre Library
folder. You can use plain old rsync to perform the synchronization from the Ansible controller to the Library branch, although you will find the included synchronize.yaml
playbook easier to use. To synchronize the remote library branches with your local library copy:
ansible-playbook -i inventories/example/hosts playbooks/synchronize.yaml
Whenever you make a change to your local library from within the Calibre GUI, simply run the above synchronize.yaml
playbook again. Adding a second Library branch is straightforward: prepare its hardware as above, and then add it to your Ansible inventory. Future synchronizations will sync all Library branches in parallel.
Occasionally, the Calibre Content server may not notice the new additions after a sync. This most often happens when a patron is actively browsing the Library while a synchronization process is updating the Calibre Library's metadata.db
database file. Letting the Library remain idle for a little while (an hour?) will often be enough to refresh the books list, but you can optionally follow a synchronization by restarting the Calibre Content server's service like so to flush the old database out of memory and reload the updated one:
ansible -i inventories/example/hosts --become -m service -a "name=calibre@main.service state=restarted"
Future visits to the Library should now show the newly synchronized Library contents to all visitors.
To allow uploads to your Library branch computer, you can enable "onionshare_receiver_mode
" on one or more of your Library branch hosts. To keep things simple, we recommend that you enable this mode on only one of your Library branch computers. The inventories/example/host_vars/raspberry.local.yaml
file provides an example configuration for the example inventory setup discussed in this README. It looks something like this:
# Example host-specific inventory variable file for `raspberry.local`.
---
onionshare_receiver_mode: true
disk_quotas_users:
- name: "{{ onionshare_username }}"
block_hard: 5G
The important line is onionshare_receiver_mode: true
, which will enable provisioning of the OnionShare server in receive mode. The second item, the disk_quotas_users
list, defines the disk space usage limit for the Operating System user account under which the OnionShare server runs. This prevents files that anonymous users upload from using more than the permitted amount of space on the Library branch filesystem. It is set to 5 gibibytes by default, but you can adjust this if you wish.
Once deployed, you can find the Onion service's address in the systemd journal for your Library branch's onionshare.service
unit:
# Using an Ansible ad-hoc command:
ansible raspberry.local -i inventories/example/hosts --ask-pass -m "shell" -a "journalctl --unit onionshare.service | grep -A 1 'Give this address to the sender'"
# Or using plain old SSH:
ssh pi@raspberry.local journalctl --unit onionshare.service | grep -A 1 'Give this address to the sender'
Of course, allowing anonymous users to upload files to your Library branch computer means that an attacker could upload a malicious file such as malware or a virus that will take over the computer that opens the file, so please take precautions and educate yourself about the risks and mitigations available to you before accepting files from others.
An ounce of prevention is worth a pound of cure. In other words, try to avoid adding files to your Library that contain viruses or malware in the first place. The best way to do this is to receive files from trusted sources and to closely inspect each file you intend to add to your Library before you add it. You can inspect files manually, or using any number of anti-virus tools and scanners.
Alternatively, you can use the provided scan-library.sh
utility. It is a small Bash shell script that finds all significant files within a Calibre Library folder and scans them against more than sixty anti-virus scanners by uploading the file to the VirusTotal Web site, which provides a public API. Note that this will provide VirusTotal with a copy of your library contents, so do not use this method if you have included sensitive or private texts in your Library. To use this script, you will need to install the free VirusTotal command-line tool, vt
. Once installed, use bin/scan-library.sh --help
for usage information.
🚧 TK-TODO: Some advice on how to set up a local development environment for this playbook given the above preamble.