Minimal web interface for managing text books.
The main purpose of this tool is to exercise podman and podman-compose.
This repository contains the following containers:
db
: Database server.web
: Web server.
First, generate database role passwords and save them as Podman secrets:
# generate passwords for the `postgres` and `bookman_web` database
# roles and save them as secrets
for i in bookman_{postgres,web}_password; do
# generate random password and save it as a secret
dd if=/dev/urandom bs=18 count=1 status=none | base64 | podman secret create $i -
done
Note: Reading 18 bytes from /dev/urandom
and base64-encoding them
produces a 25-digit password with 144 bits of entropy, which should be
enough for anybody ;).
To start the containers:
# build (if necessary) and start containers, run in background
podman-compose up -d
First run caveats:
- If you have not run this service before, the command above will build and tag images for both containers. The tags will be re-used for subsequent boots.
- The first time the
db
container boots it will seed the database with several books from Project Gutenberg. This may take a few moments. Subsequent boots will be faster.
Once the service is initialized, port 3000 of the web
container is
exposed on the host and accessible via a web browser.
You can monitor the logs with podman-compose logs -f
.
To stop the service:
# shut down bookman containers
podman-compose down
Although Bookman is primarily a podman-compose test, there are a few technical aspects that may be of interest.
- Bulma (custom build to reduce size)
- Icons from Bootstrap Icons
- Minified with esbuild (see
web/assets/build.rb
).
Static web assets are minified and served compressed to keep the initial payload size below 20 kB.
The web server itself is a staticly-linked binary built via a
multi-stage build. Web assets are minified and embedded into the
binary via go embed. As a result, the web
container consists of a
single 9MB /bookman
binary:
> cd web && podman build -t bookman-web .
...
> podman unshare
> cd $(podman image mount bookman-web)
$ ls
bookman
$ du -h ./bookman
9.0M ./bookman
The web interface sets a restrictive Content-Security-Policy. The complete list of security-related HTTP response headers is as follows:
Access-Control-Allow-Methods
Content-Security-Policy
Cross-Origin-Opener-Policy
Cross-Origin-Resource-Policy
Permissions-Policy
Referrer-Policy
X-Content-Type-Options
X-Frame-Options
Because this site might be served locally or behind a reverse proxy, it does not set the following headers:
Access-Control-Allow-Origin
Strict-Transport-Security
See SecurityHeadersMiddleware
in web/middleware.go
for additional
details.
This site does not use cookies, local storage, or session storage.
The database server is Postgres.
The underlying bookman
database and database objects are owned by the
bookman_sys
database role rather than postgres
.
Queries from the web interface run as the bookman_web
database role,
which has relatively limited privileges.
The underlying books
table has an indexed tsvector
column which is
generated from the name, author, and content of each uploaded book.
Searches are performed against the index.
See db/scripts/books.txt.gz
for additional information. Note:
books.txt.gz
also contains the contents of seed books from Project
Gutenberg, so it is quite large.