Kokkoja: first public assembly

Assembled from the development repository by tools/make_release.sh.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
ackandClaude Fable 5.1 committed 2026-10-06 15:58:02 +02:00
commit 95c80d04c9
39 files changed
+9905

No files matched your search

+69
View File
@@ -0,0 +1,69 @@
# The Kokkoja server
A Kokkoja *server* is one machine that does two things for a group of
users: it runs the relay, which passes the participants' encrypted
messages, and it serves the client as a web site, so that a guest can
join by opening a link with nothing installed. Neither stores anything.
The first one is Andy's VPS at `kokkoja.andykopra.com`.
| On the server | What it is |
|----------------------------|-------------------------------------------------|
| `/opt/kokkoja/relay/` | the relay's code (`relay/` in this repository) |
| `/opt/kokkoja/venv/` | Python with the `websockets` library |
| `/var/www/kokkoja/` | the client (`client/`) and its `config.json` |
| `kokkoja-relay.service` | the relay, run as the system user `kokkoja`, on 127.0.0.1:8770 |
| nginx site `kokkoja` | `https://<site>/` serves the client; `wss://<site>/relay` reaches the relay |
The client's `config.json` on the site says
`{"relay": "wss://kokkoja.andykopra.com/relay", "storage": "browser",
"site": "https://kokkoja.andykopra.com/"}`: the notebooks of anyone using
the site are kept in their browser. A host with the local program uses
the server with `python3 kokkoja.py --relay wss://kokkoja.andykopra.com/relay`;
the invitation is then a link to the site.
## Setting up or updating
From the project directory on a development machine:
deploy/deploy.sh
copies the relay, the client and the files in this directory to the
server and runs `setup_vps.sh` there, which makes or updates everything
in the table and restarts the relay. It can be run again at any time.
`KOKKOJA_SERVER` names another ssh destination than
`root@vps313847.iceservers.net`.
Steps a person does, once:
1. DNS: an A record `kokkoja.andykopra.com` pointing at the VPS
(82.221.141.89), in cPanel's Zone Editor, since andykopra.com is on
the orangewebsite.com nameservers. Check with
`host kokkoja.andykopra.com buffy.ns.orangewebsite.com`.
2. TLS, after the name resolves and `deploy.sh` has run once:
ssh root@vps313847.iceservers.net certbot --nginx -d kokkoja.andykopra.com
certbot adds the TLS listener to the nginx site and turns the plain
HTTP block into a redirect. Afterwards `setup_vps.sh` leaves the
installed nginx site alone, so a later change to `nginx-site.conf`
must be carried over by hand.
3. The VPS sentinel sees a new service, a new user, a new nginx site and
a new listening socket. After deploying, as root on the VPS:
vps-sentinel --accept
Checks: `https://kokkoja.andykopra.com/config.json` shows the
configuration above; `systemctl status kokkoja-relay` is active;
`journalctl -u kokkoja-relay -n 20` shows it listening.
## Security
The relay sees only ciphertext and room ids and keeps nothing. It runs
as a user of its own under systemd's sandboxing (read-only system, no
home directories, no new privileges). The site sends a Content Security
Policy that lets the client load nothing from anywhere else and forbids
framing. What the site cannot protect against is itself: whoever
controls the server could serve a changed client to browser-only
guests. Participants running the local program are not exposed to that.
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
# Deploy the Kokkoja server: copy the relay, the client and the server
# files to the VPS, then run deploy/setup_vps.sh there. Run from the
# project directory on a development machine.
#
# deploy/deploy.sh
#
# KOKKOJA_SERVER names the ssh destination (default root@vps313847.iceservers.net).
# The site's config.json is written here: the relay and site addresses
# below are the server's public names.
set -euo pipefail
cd "$(dirname "$0")/.."
server=${KOKKOJA_SERVER:-root@vps313847.iceservers.net}
site=https://kokkoja.andykopra.com/
relay=wss://kokkoja.andykopra.com/relay
staging=$(mktemp -d)
trap 'rm -rf "$staging"' EXIT
mkdir -p "$staging/relay" "$staging/client" "$staging/deploy"
cp relay/kokkoja_relay.py relay/requirements.txt "$staging/relay/"
rsync -a --exclude '__pycache__' client/ "$staging/client/"
printf '{"relay": "%s", "storage": "browser", "site": "%s"}\n' "$relay" "$site" > "$staging/client/config.json"
cp deploy/kokkoja-relay.service deploy/nginx-site.conf deploy/setup_vps.sh "$staging/deploy/"
ssh "$server" 'mkdir -p /opt/kokkoja/incoming'
rsync -a --delete "$staging/" "$server:/opt/kokkoja/incoming/"
ssh "$server" 'bash /opt/kokkoja/incoming/deploy/setup_vps.sh'
+29
View File
@@ -0,0 +1,29 @@
# The Kokkoja relay as a system service. Installed by deploy/setup_vps.sh
# as /etc/systemd/system/kokkoja-relay.service.
#
# The relay listens on this computer only; nginx terminates TLS and
# forwards wss://<site>/relay/... to it (deploy/nginx-site.conf).
[Unit]
Description=Kokkoja relay
After=network.target
[Service]
User=kokkoja
Group=kokkoja
ExecStart=/opt/kokkoja/venv/bin/python /opt/kokkoja/relay/kokkoja_relay.py --host 127.0.0.1 --port 8770
Restart=on-failure
RestartSec=5
# The relay needs nothing on the system but its own code.
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_INET AF_INET6
MemoryDenyWriteExecute=yes
[Install]
WantedBy=multi-user.target
+39
View File
@@ -0,0 +1,39 @@
# nginx server block for a Kokkoja web site: the client's files at
# https://kokkoja.andykopra.com/ and the relay at wss://kokkoja.andykopra.com/relay.
# Installed by deploy/setup_vps.sh as /etc/nginx/sites-available/kokkoja.
#
# This is the plain HTTP form. certbot --nginx -d kokkoja.andykopra.com
# adds the TLS listener and turns this block into a redirect.
server {
listen 80;
server_name kokkoja.andykopra.com;
root /var/www/kokkoja;
index index.html;
# The relay. The client connects to <relay address>/room/<room id>,
# so /relay/room/<id> here becomes /room/<id> at the relay.
location /relay/ {
proxy_pass http://127.0.0.1:8770/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-For $remote_addr;
# The relay pings every 20 seconds; a quiet lesson must not be cut off.
proxy_read_timeout 1h;
proxy_send_timeout 1h;
}
location / {
try_files $uri $uri/ =404;
}
# The client is a few small files that change with every deployment:
# browsers ask whether they have changed rather than keeping old ones.
add_header Cache-Control "no-cache" always;
# The client loads nothing from anywhere else, and may not be framed.
add_header Content-Security-Policy "default-src 'self'; connect-src 'self' wss://kokkoja.andykopra.com; img-src 'self' blob: data:; style-src 'self' 'unsafe-inline'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer" always;
}
+63
View File
@@ -0,0 +1,63 @@
#!/bin/bash
# Set up, or bring up to date, the Kokkoja server on this machine. Run as
# root ON THE SERVER, after deploy/deploy.sh has copied the files to
# /opt/kokkoja/incoming. Safe to run again: every step checks first.
#
# What it makes:
# /opt/kokkoja/relay/ the relay's code
# /opt/kokkoja/venv/ Python with the websockets library
# /var/www/kokkoja/ the client, with config.json
# kokkoja-relay.service the relay, as the system user "kokkoja"
# nginx site "kokkoja" the web site and the relay behind TLS
#
# TLS is the one step left to a person, once the name resolves to this
# machine (see deploy/README.md):
# certbot --nginx -d kokkoja.andykopra.com
set -euo pipefail
incoming=/opt/kokkoja/incoming
test -d "$incoming" || { echo "nothing in $incoming: run deploy/deploy.sh first" >&2; exit 1; }
if ! id kokkoja >/dev/null 2>&1; then
useradd --system --no-create-home --shell /usr/sbin/nologin kokkoja
fi
# Python with the websockets library, in a virtual environment of its own.
if ! python3 -c 'import ensurepip' >/dev/null 2>&1; then
apt-get install -y python3-venv
fi
if [ ! -x /opt/kokkoja/venv/bin/python ]; then
python3 -m venv /opt/kokkoja/venv
fi
/opt/kokkoja/venv/bin/pip install --quiet --upgrade pip
/opt/kokkoja/venv/bin/pip install --quiet -r "$incoming/relay/requirements.txt"
# The relay's code, and the client.
rm -rf /opt/kokkoja/relay
cp -r "$incoming/relay" /opt/kokkoja/relay
chmod -R a+rX /opt/kokkoja/relay
mkdir -p /var/www/kokkoja
rsync -a --delete "$incoming/client/" /var/www/kokkoja/
chmod -R a+rX /var/www/kokkoja
# The service.
install -m 644 "$incoming/deploy/kokkoja-relay.service" /etc/systemd/system/kokkoja-relay.service
systemctl daemon-reload
systemctl enable --quiet kokkoja-relay.service
systemctl restart kokkoja-relay.service
# The nginx site. Once certbot has edited the installed copy, it is not
# replaced: certbot's additions would be lost.
if [ ! -e /etc/nginx/sites-available/kokkoja ]; then
install -m 644 "$incoming/deploy/nginx-site.conf" /etc/nginx/sites-available/kokkoja
elif ! grep -q "managed by Certbot" /etc/nginx/sites-available/kokkoja; then
install -m 644 "$incoming/deploy/nginx-site.conf" /etc/nginx/sites-available/kokkoja
else
echo "nginx site left as certbot made it; compare with deploy/nginx-site.conf by hand"
fi
ln -sf ../sites-available/kokkoja /etc/nginx/sites-enabled/kokkoja
nginx -t
systemctl reload nginx
systemctl --no-pager --lines=3 status kokkoja-relay.service || true
echo "Kokkoja server is set up."