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

+244
View File
@@ -0,0 +1,244 @@
# A guest's Kokkoja
This part describes Kokkoja as a guest sees it: someone invited to a
session by its host. In a lesson, the guest is the student. The host's
screen is the same with a few more buttons, described in
[A host's Kokkoja](host.md).
## The invitation
The host sends you an invitation by whatever means you both use: email, a
chat message, a text message. It is a link of this shape:
https://kokkoja.andykopra.com/#join=wss://kokkoja.andykopra.com/relay%23uQ5c__CGB4FvN64Qiv3jGA.aS_1O51Umk1YHXwgqBZANw
The part after `join=` is the invitation itself, which the host's Kokkoja
may also give you on its own, without the link around it. Both forms
work wherever an invitation is asked for. The invitation carries the key
that protects the session, so pass it on only to people who belong in the
session, and treat it as you would a meeting link. An invitation is good
for one session: the host's next session has a new one.
## Joining
Open the link in a web browser. Firefox, Brave and Chrome are known to
work; other browsers built on the same engines very probably do. You see
Kokkoja's start screen with the invitation already filled in. Enter the
name the others should see and press **Join**. That is all: nothing is
installed, and there is no account to make.
Your name is remembered by the browser for next time. So is a second
field, **Notebook for the pages of this session**, which you can leave as
it is for now; what it does is explained under "Your notebook" below.
If you have the Kokkoja program installed on your computer (see
[A host's Kokkoja](host.md) for what that is and why you might want it),
the start screen also offers to open the invitation in it instead. If
you have not, ignore that line.
If the host has not started yet, or has left, you are still in the
session. The page is shared among whoever is present, and the host's
place in it waits for the host to return.
## The screen
Across the top is the toolbar. On the left are the tools and their
settings; on the right, the zoom and, for the host, the buttons that
start pages. Below it, the page fills most of the window, with a panel on
the right. Every button explains itself: rest the pointer on it for a
second and its explanation appears beneath it.
### The page
The page is an A4 sheet, shown so that it fits the window. Write
anywhere on it. When your writing comes near an edge, the sheet grows in
that direction, in small steps, so there is always room, and the view
keeps its scale and extends beyond the window; scroll to reach the rest.
An image the host puts on the page lies at the top, or below the images
already there.
The page as a whole is shared: everyone sees the same marks in the same
places. Your view of it is your own: how far it is zoomed and where it
is scrolled to concern nobody else.
### Pen
**Pen** is chosen when you arrive. Draw with a pen on a tablet, a mouse,
or a finger on a touch screen. With a pen, pressing harder makes the
line wider. The colours are to the right of the tools: black, blue, red
and green, and **+** opens a palette of more. A colour chosen from the
palette joins the row of colours and stays there in this browser. The
three lines to the right of the colours are the line widths: 0.4, 0.7
and 1.2 mm at ordinary pressure.
If a pen is in use, a hand resting on a touch screen is ignored.
### Text
**Text** types on the page. Tap where the text should begin and type; a
text field appears there and the text is laid out on the page as you
type. Enter begins a new line. Press Escape, or tap somewhere else, to
finish; a block with nothing in it disappears.
A block made by a tap does not wrap: its lines are as long as you type
them, and the page grows to hold them. To make the text wrap at a chosen
width, drag along where its top edge should run instead of tapping: the
block begins at the left end of the drag and wraps at the right end.
Tap your own text to change it. While a block of yours is being edited
it is drawn grey, and three handles appear: the one at its top-left
corner and the line along its top edge move it, the one at the right end
of that line changes its width. You cannot change anyone else's text.
With the Text tool, the row of colours stays and the line widths give way
to the font, one of **Sans**, **Serif** and **Mono**, and the size in
points, 14 unless you change it. Both apply to the block you are typing
and to the next ones.
### Eraser
**Eraser** removes your own writing. Move it across a stroke and the
whole stroke goes; across a text block and the block goes. It removes
nothing of anyone else's, and nobody else's eraser can touch yours. An
erased stroke is gone from the page for everyone.
### Zoom
**Fit** shows the whole page in the window, and keeps doing so as the
page grows by writing, except while you are in the middle of a stroke of
your own. **+** and **−** zoom in and out by a tenth. Holding Ctrl and
turning the scroll wheel zooms about the pointer. When the page is
larger than the window, scroll it as you would any page. When the host
adds an image, the view keeps its scale rather than shrinking to fit; a
new image at the bottom of a long page is reached by scrolling.
If you reload the browser tab, it comes back to the page as you last saw
it: same zoom, same place.
### The panel
**Participants** lists everyone in the session, with *you* and *host*
marked, and *away* beside anyone who has left or lost their connection;
an absent participant's writing stays on the page. The box in front of
each name shows or hides that participant's writing on your screen alone.
Hiding your teacher's corrections to try an exercise again, or hiding
your own answers to see the exercise as it was, changes nothing for
anyone else.
**Invitation** shows the invitation of this session, with a button to
copy it, in case someone else should be invited.
**Current notebook**, **Keep page** and **Export** concern your notebook,
below. Under them, a line reports what was last saved, and whether
anything went wrong.
The last line of the panel reports what your pen or mouse is delivering:
the kind of device, the pressure, and how many positions per second it
sends. It is there for curiosity and for finding faults.
### Status
At the right end of the toolbar, **Page 3** is the number of the page you
are on, counted from the start of the session, and beside it *Connected*,
*Connecting…* or *Disconnected* says how you stand with the relay. If
the connection drops, Kokkoja reconnects by itself; what you wrote while
disconnected is sent when it returns. Until the host has started a
page, the toolbar says *Waiting for the host's page*.
## Your notebook
Every page of the session is saved for you, as it changes, without your
doing anything. Where it is saved depends on how you joined.
### In the browser
When you joined by opening the link, your notebook is kept inside the
browser, on the computer you are using. It stays there between sessions,
but it belongs to that browser: another browser, or another computer,
has its own. The page you are on is saved there continuously. When the
session moves to another page, the page you are leaving is kept under a
dated name, such as `2026_10_06_01.svg`, the date being the day it was
last changed and the number counting the pages kept that day.
Two buttons bring pages out of the browser:
* **Keep page** keeps a dated copy of the page as it is now, and
downloads it, so you find it among your browser's downloads. Pressing
it again before anything has changed makes no second copy.
* **Export** downloads a picture of the page as you see it, as a PNG
file; writing you have hidden is left out. Its name is the date and
the page number.
A page file is an SVG file. Any web browser displays it, and vector
drawing programs such as Inkscape open it. Kokkoja itself can load one
again, writing and all, when someone with the right to start pages
chooses it (see "When the host gives you more" below, and the host's
part).
The notebook field on the start screen names the notebook within the
browser's storage. Giving each course or each colleague a notebook of
their own keeps their pages apart; the name proposed is simply
*Notebook*. **Current notebook** in the panel shows the notebook in use
and lets you change it during the session.
### On your computer
If you would rather have your pages as files in a directory on your own
computer, where you can open, copy and back them up as you do any file,
install the Kokkoja program. It is small and needs only Python, which
Linux and macOS have or can quickly get; [A host's Kokkoja](host.md)
tells how, since every host needs it. Then join from the program's own
start screen, pasting the invitation, or follow the start screen's offer
to open the invitation in the installed program.
With the program, a notebook is a directory in `Kokkoja` in your
Documents directory. The page you are on is `Current.svg` there, and
kept pages lie beside it under their dated names. **Keep page** makes
the dated copy; **Export** opens Kokkoja's own file chooser, in which you
pick a directory and a name for the PNG file.
The program also keeps your identity, so that you are the same
participant in every session and from any browser on that computer, and
writing from an earlier lesson is still yours when its page is loaded
again. In the browser, your identity belongs to that browser.
## Leaving and returning
Close the tab and you have left. Nothing needs saving. If you come back
to the same session by opening the invitation again, your writing is
still on the page, from the others' copies and from your notebook, and
it is still yours to erase or change. A tab that was in a session and is
reloaded goes straight back in.
## When the host gives you more
Normally only the host puts images on the page, starts new pages, or
loads pages from a notebook. The host can give you the same right, for
instance in a discussion among colleagues where everyone brings
material. When that happens, three more buttons appear at the right of
your toolbar:
* **New** starts a new blank page for everyone. The page being left is
kept in each person's notebook.
* **Image** puts an image file on the page for everyone, below any
images already there. Your browser's file chooser asks for the file
(with the Kokkoja program, Kokkoja's own chooser does, beginning where
the notebooks are, with a choice whether an image wider than the page
is shrunk to fit it). A PNG or JPEG file keeps its exact pixels unless
it is very large; other formats and very large images are converted
and reduced before they are shared. An image is placed at its natural
size, centred, 8 mm from the top or from the image above; one wider
than the sheet is scaled down to fit between the margins. To put an
image on a page of its own, press **New** first.
* **Notebook** starts a new page for everyone from a page kept in one of
your notebooks, with everything that was written on it. A chooser
shows your notebooks and small pictures of their kept pages. The kept
page itself is not changed: the session works on a copy.
An image cannot be moved or removed once anyone has written on it or
added anything after it, since the marks on it would make no sense
without it. The one exception is a mistake caught at once: the person
who added an image can take it back with a tap of the eraser on it, as
long as nothing else has happened on the page since.
The right stays with you until the host withdraws it or the session ends.
+211
View File
@@ -0,0 +1,211 @@
# A host's Kokkoja
The host starts a session, invites the guests and drives the session:
puts images on the page, starts new pages, loads pages from a notebook,
and decides which guests may do the same. In a lesson, the teacher is
the host. Everything in [A guest's Kokkoja](guest.md) applies to the host
too; this part describes what is added, and how to install the Kokkoja
program, which a host needs.
## Installing the Kokkoja program
A guest can join from a web site with nothing installed. A host runs the
Kokkoja program on their own computer. It is a small Python program with
two jobs: it shows Kokkoja in your web browser, and it reads and writes
your notebooks as ordinary directories and files. It listens only to
your own computer and talks to nothing else but the relay you name.
You need:
* **Linux or macOS.** Windows has not been tried yet.
* **Python 3.** Linux has it. On macOS, open Terminal and type
`python3 --version`; if Python is not installed, macOS offers to
install the command line developer tools, which include it. Any Python
from 3.9 on will do.
* **A web browser.** Firefox, Brave or Chrome; other browsers built on
the same engines very probably work.
* **A pen tablet**, if you want to write by hand. A mouse works for
pointing and for rough marks.
Get Kokkoja from its repository, either by downloading it as a zip file
and unpacking it, or with git:
git clone https://git.andykopra.com/ack/kokkoja-rel.git kokkoja
Nothing else is installed and nothing is changed on your system. To
update later, download again or run `git pull` in that directory. To
remove Kokkoja, delete the directory; your notebooks are elsewhere and
stay.
## Starting Kokkoja
Open a terminal in the Kokkoja directory and run
python3 kokkoja.py --relay wss://kokkoja.andykopra.com/relay
The program prints an address beginning `http://localhost:8760/` and
opens your browser there. Leave the terminal open: the program must run
as long as you are in a session. Ctrl-C stops it.
The address the program prints contains a secret token. Your browser
remembers it, so afterwards `http://localhost:8760/` alone brings Kokkoja
back; but if the browser ever says that the notebooks are locked, open
the full address the program printed. The token is what stops any other
web page you might have open from reading your notebooks.
The relay address names the *Kokkoja server* you use. A server does two
things: it passes the participants' messages, encrypted so that it
cannot read them, and it serves the web site that your guests join from.
The address above is Andy's server; whoever runs a server for you gives
you its address. If you give the program no relay, it expects one on
your own computer, which is useful only for trying Kokkoja alone.
Other options, all optional:
* `--notebooks DIRECTORY` keeps the notebooks somewhere other than
`Kokkoja` in your Documents directory.
* `--port NUMBER` uses another port than 8760, if that one is taken.
* `--no-browser` does not open a browser; open the printed address
yourself.
* `--site ADDRESS` names the web site that invitations link to. You
need it only if your server's web site is not at the relay's host.
## Starting a session
On the start screen, enter your name and the notebook for this session,
and press **Start a session**. You are on page 1, a blank sheet, and the
panel on the right shows the invitation.
### The notebook
The notebook you name is a directory under `Kokkoja` in your Documents
directory, made when it is first needed, and the start screen offers the
names of the notebooks you already have. For teaching, a notebook per
student works well: everything a student is given, with everything they
and you wrote on it, gathers in that student's notebook, and the next
lesson with that student starts by naming it again. Your library of
exercises can live in notebooks of its own, such as *Exercises A1*, filled
by keeping pages in a session of your own with nobody else present.
The notebook can be changed during a session, in **Current notebook** in
the panel. The page you are on then continues in the new notebook.
### The invitation
**Invitation** in the panel shows a link. **Copy** copies it; send it to
your guests by email, in a chat message, or however you reach them. A
guest opens it, enters a name and is in. A guest with the Kokkoja
program installed may paste it into their own program instead. The link
carries the secret that protects the session, so send it only to those
who should be there; anyone with the link can join and read the page.
Each session you start has a new invitation.
An invitation is good as long as you keep the session open. A guest can
join at any time, see everything written so far, and leave and return.
## Driving the session
Three buttons at the right of the toolbar are the host's.
**New** starts a new blank page for everyone. Pages are numbered from 1
through the session. The page being left is kept in every participant's
notebook, by itself, under a dated name; nobody has to remember to save.
**Image** puts an image file on the page for everyone. Kokkoja's own
file chooser opens, beginning where your notebooks are and remembering
where you last chose from; a directory path can also be typed into it.
It shows small pictures of the image files it finds. Choose one and it
is placed on the page: the first at the top of the sheet, each further
one 8 mm below the lowest image there, centred, at its natural size. An
image wider than the sheet is scaled down to fit between the margins, so
a full-page A4 scan appears at about 92%; the chooser has a setting to
turn that off, in which case the sheet widens to the image instead. PNG
and JPEG files are shared as they are unless very large; other formats,
and images over 2400 pixels on a side or 1.5 MB, are converted to JPEG
and reduced first. A page holds up to 12 images.
For a page that begins with an image, press **New** and then **Image**.
For a series of exercises, keep pressing **Image** on the same page: the
sheet grows downward and everyone scrolls through them. A new image does
not change anyone's zoom; it appears below, to be scrolled to.
If you chose the wrong file, a tap of the eraser on the image takes it
back, as long as nothing else has happened on the page since: no stroke,
no text, no erasure, no further image, by anyone. After that an image
stays, because the marks made on it would make no sense without it.
**Notebook** starts a new page for everyone from a page kept in one of
your notebooks, with everything that was written on it. A chooser shows
your notebooks on the left and small pictures of the chosen notebook's
kept pages on the right. The loaded page is a copy: the kept page is
not changed by anything that happens next. Each layer on it still
belongs to the participant who wrote it, so a student's answers from an
earlier lesson are still theirs, and your corrections still yours.
This is also how you return to an earlier page of the session: the page
was kept when you left it, and **Notebook** brings it back as a new page.
A lesson might go: **New**, **Image** for the exercise, work, **New**
for a point of grammar, work, **Notebook** to return to the exercise.
## Guests' rights
In the **Participants** list, each guest's name has a button **pages** in
front of it. Pressing it lets that guest start new pages, put images on
the page and load pages from their notebook, exactly as you can; the
three buttons appear on their toolbar. Pressing it again withdraws the
right, which otherwise lasts until the session ends. Guests can never erase or
change anyone's writing but their own; that needs no setting.
## Hiding writing
The box in front of each participant's name shows or hides that
participant's writing on your screen only. Hide a student's answers to
see the exercise clean, hide your own corrections to see what the
student sees, show them again; the others notice nothing. **Export**
leaves hidden writing out of the picture it makes, which is how to make a
picture of the exercise with the corrections alone.
## Keeping and exporting
The page you are on is written continuously to `Current.svg` in the
current notebook, and copied to a dated file whenever the session moves
on. **Keep page** makes such a copy now, named for the day and a serial
number, such as `2026_10_06_02.svg`; pressing it again before anything
has changed makes no second copy. Use it to mark a state worth keeping
on its own, or to add a finished page to a notebook of exercises.
**Export** saves a picture of the page as you see it, as a PNG file at
200 dots per inch, in a directory and under a name you choose in
Kokkoja's file chooser. Replacing an existing file takes a second press
of the button, so a slip does not overwrite anything.
The notebooks are ordinary directories, and a page is an ordinary SVG
file. Open one in a browser or in Inkscape, attach it to an email, keep
it with your other documents. Only Kokkoja can load it as a live page
again, with each person's writing still theirs.
## Ending a session
Close the browser tab, or stop the program with Ctrl-C in its terminal.
There is nothing to save: the page is already in your notebook, and in
each guest's. Guests who are still connected can go on writing on the
page among themselves, but no new page can be started without you or a
guest you have given the right, and a guest who arrives after everyone
has left finds an empty session. The next lesson is a new session with a
new invitation; load the pages you need from the student's notebook.
## When something goes wrong
* **The browser says the notebooks are locked.** Open Kokkoja from the
full address the program printed when it started, the one with the
token.
* **"Disconnected" in the toolbar.** Kokkoja reconnects by itself as
soon as the relay can be reached, and sends what was written meanwhile.
If it does not come back, check your connection; the page is safe in
your notebook either way.
* **A guest sees "Waiting for the host's page".** You have not started
a page yet, or your connection is down. The first page is started when
you start the session, so this usually means the latter.
* **Pressure does not work.** The last line of the panel reports what
the browser receives from your pen. If it says *mouse*, the browser is
not seeing the pen as a pen; try the other browser.
+234
View File
@@ -0,0 +1,234 @@
# How Kokkoja works
This part is for the curious, for anyone who wants to run a Kokkoja
server, and for anyone who wants to read or change the code. It
describes the parts, what travels between them, what is protected and
what is not, and how pages are stored. The code is the final word; it
is short and commented.
## The parts
Kokkoja is three programs and a few static files.
* **The client** (`client/`) runs in the web browser and does everything
the user sees: the pen, the page, the text, the encryption, the
saving. It is plain JavaScript modules, run by the browser as written,
with no build step and no third-party packages. Its types are written
in JSDoc comments and checked by the TypeScript compiler during
development.
* **The local program** (`kokkoja.py`) runs on the user's computer. It
serves the client to the browser at `http://localhost:8760/`, keeps the
user's identity, and reads and writes the notebook directories, which
a web page cannot do on its own. It uses nothing but Python's standard
library.
* **The relay** (`relay/kokkoja_relay.py`) runs on a server that every
participant can reach. It passes messages between the members of a
session and nothing more: it cannot read them and it stores nothing.
It is Python with one library, `websockets`.
* **The web site** is the `client/` directory served over HTTPS, with a
`config.json` beside it, so that a guest can join without the local
program. `deploy/` has the server setup.
Every participant's computer holds the whole page. Only changes travel:
a stroke as it is drawn, a text block as it is typed, an erasure, a new
page, an image once. There is no central copy.
## Why the relay exists
Two computers behind home routers generally cannot connect to each other
directly, so something in between has to pass the messages. The relay is
the smallest such thing: a WebSocket server with *rooms*. A client
connects to `wss://<server>/relay/room/<room id>`; the relay gives the
connection a number, tells the room that a peer has arrived, and from
then on sends every message it receives from one member to all the
others, stamped with the sender's number. When the last member leaves,
the room is gone. It holds at most 32 members per room and 500 rooms,
and refuses messages over 1 MB; clients split anything larger.
## Sessions and encryption
The invitation is
<relay address>#<secret>.<host id>
The *secret* is 16 random bytes, made by the host's browser. From it
every client derives two values with HKDF-SHA-256: the *room id*, which
names the room at the relay, and the *message key*, a 256-bit AES-GCM
key with which every message is encrypted before it is sent and
decrypted on arrival. The relay sees the room id, the members' network
addresses, and the size and timing of encrypted messages. It cannot
compute the key from the room id, so it cannot read anything. Neither
can anyone watching the network between the participants and the relay,
which is TLS-protected in any case.
A link invitation wraps the same text:
https://<site>/#join=<invitation>
Browsers never send the part of an address after `#` to the site, so the
secret stays between the host and the guests.
## Identity and layers
Each participant has a signing key pair (ECDSA on P-256), made in the
browser the first time and then kept, by the local program in its
configuration directory or by the browser in its own storage. The
*participant id* is the first 16 bytes of the SHA-256 hash of the public
key. When a client joins a room it announces itself with a signed
`hello` carrying its name and public key; the signature covers the room
id and the peer number the relay stamped on the message, so nobody can
replay it elsewhere or claim another peer's connection. Until a peer has
sent a valid `hello`, everything else from it is ignored.
A page is a stack of layers, one per participant, keyed by participant
id. A client accepts a change to a layer only from the participant whose
id it is, so the rule "nobody can change anyone else's writing" is not a
permission that could be misconfigured but a consequence of how the data
is organised. Because the id is derived from the key, and the key is
kept, a person is the same participant in every session from the same
computer, and their writing on a page loaded weeks later is still theirs.
The host is whoever holds the key whose id is in the invitation. Pages,
images and the list of guests allowed to start pages are accepted only
from the host (or from an allowed guest, for pages and images).
## Pages
Page coordinates are in units of 0.1 mm. A blank page is an A4 sheet,
2100 by 2970 units. Writing within 8 mm of an edge grows the sheet by
8 mm on that side. An image is placed at its natural size, taken from
the resolution recorded in the file, or 0.1 mm per pixel when there is
none, centred across the sheet, 8 mm from the top or from the image
above, scaled down if wider than the sheet inside its margins (or, if the
person adding it chooses, placed at natural size with the sheet widened
to hold it). A page holds at most 12 images and 24 MB of image data.
An image file over 2400 pixels on a side or 1.5 MB, or not PNG or JPEG,
is redrawn at most 2400 pixels and stored as JPEG before it is shared;
smaller PNG and JPEG files are shared byte for byte.
A stroke is a list of points with pressure, and the line width is a
function of pressure: nominal at a pressure of 0.4, about 2.6 times that
at 0.75. Points are sent while the stroke is drawn, so the others see it
grow. A text block is sent whole at every keystroke, with its line
breaks already laid out by the writer's browser, so that everyone shows
the same lines.
Each page has a number, counted from 1 in the session, and a random id.
A client that joins late asks the longest-connected member for the
layers of absent participants, and receives each present participant's
layer from that participant. A client that reconnects after a dropped
connection sends its whole layer again, so that nothing drawn meanwhile
is lost.
## The page file
A notebook is a directory. The page being written is `Current.svg` in
it, rewritten a moment after every change (at most every 400 ms while
writing continues, at least every 2 s). A *kept* page is a copy of
`Current.svg` named `YYYY_MM_DD_NN.svg`, the day being when the page was
last changed and NN a serial number within that day. A copy is made when
the user asks (**Keep page**), and by the program whenever `Current.svg`
is about to be given to another page: when the session moves to a new
page, when the notebook is changed, and when a session begins and finds
a page of another session in the notebook. Nothing is copied if the
page is blank or an identical copy already exists.
The file is an SVG document that any viewer displays: the images as
embedded `<image>` elements, each layer as a group with the participant's
name and id, each stroke as a path and each text block as text.
Beside the shapes, the same elements carry Kokkoja's own data in
attributes, the exact points and pressures, the text and its breaks, so
that Kokkoja reads the page back without loss, with each layer still
belonging to its participant. The session id and host id are recorded
too, which is how a reloaded tab knows that `Current.svg` is its own page
to continue from.
With the local program, the notebooks are in `Kokkoja` in the Documents
directory (`--notebooks` chooses another place), the identity and the
browser's token in the configuration directory (`~/.config/kokkoja` on
Linux, `~/Library/Application Support/kokkoja` on macOS). In the
browser, the notebooks are in the site's IndexedDB under the same names
and rules, and the identity in its localStorage; a kept page and an
exported picture are offered as downloads, since that is the only way a
web page can hand a file to its user.
## The local program and its token
The local program listens on 127.0.0.1 only. Any web page open in the
browser could nevertheless send requests to `localhost:8760`, so every
request that touches files must carry a secret token, which the program
makes once, keeps in its configuration directory, and passes to the
browser in the address it opens (`#token=...`, which the browser keeps
in its local storage and never sends to any server). Requests without
the token are refused. The program also checks that requests come from
its own origin. Outside the notebooks it reads only image files and
writes only PNG files, each where the file chooser was pointed.
## The web site, and whom you trust
A client served by a web site is a web application like any other: the
site operator's code runs in your browser. The end-to-end encryption
protects the session from the relay and the network, but a dishonest
site could serve a client that gives the key away. Participants who run
the local program load the client from their own disk and are not
exposed to that; they trust the code they downloaded once. The site
sends a Content Security Policy that lets the client load nothing from
anywhere else, which protects against the ordinary web attacks but not
against the operator. For a lesson this is the same trust one places in
any video call service, and considerably less than in one that sees the
content; it is stated here so that nobody is surprised.
## Running a server
A server is a machine with a public name, TLS and nginx (or any reverse
proxy that passes WebSockets). `deploy/README.md` describes the one
Kokkoja comes with: the relay as a systemd service bound to localhost,
nginx terminating TLS, serving the client's files at `https://<name>/`
and forwarding `wss://<name>/relay/` to the relay, and a `config.json`
telling the client which relay to use and that notebooks are kept in the
browser. A host uses the server with
python3 kokkoja.py --relay wss://<name>/relay
and invitations become links to `https://<name>/`. One server serves
any number of hosts and sessions at once; the relay's work per session
is passing a few kilobytes per second. Since the relay cannot read what
it passes, a server run by someone else is a matter of availability, not
of privacy; the web site is the part that requires trust, as described
above. A teacher who wants no dependence on anyone's server needs a
machine of their own that is reachable from the Internet, which a
computer behind a home router is not without arrangement.
## What is not protected yet
* Only `hello` is signed. A participant working together with a
dishonest relay could forge another participant's strokes. Signing
every message, or page descriptors with the host's key, would close
this.
* A participant can announce a page with a higher number than the
host's, and the others move to it.
* The relay sees who talks to whom, when, and how much.
## Pen input
The client uses the browser's pointer events, which deliver every sample
a pen produces, with pressure, in Firefox and in Chromium-based browsers
on Linux and macOS. The panel's last line shows what arrives. When a
pen has been used, touches are ignored, so a hand resting on a touch
screen does not draw; this rule has not yet been tested with a device
that has both a pen and a touch sensor. Kokkoja opens none of the
browser's system dialogs (the colour palette and, with the local
program, the file chooser are its own), because one such dialog crashed
Firefox on Linux.
## Development
The code is Python and JavaScript only. The tests run with
.venv/bin/python -m unittest discover tests -v
where `tests/test_end_to_end.py` drives a headless Firefox through
geckodriver with several participants, with and without the local
program, and `npm run check` type-checks the client (the TypeScript
compiler is the only development dependency). The relay's `--host` and
`--port` and the local program's options are listed by `--help`.
+106
View File
@@ -0,0 +1,106 @@
# Kokkoja: an introduction
Kokkoja lets people at different computers write together on a shared
*page*. Everyone in a session sees the same page. Anyone can write on it
by hand or type on it, and what one person writes appears on the others'
screens as it is written. A page can begin blank, or it can begin with an
image, such as a scanned sheet or a photograph, which everyone then writes
on. Every page is saved automatically to a *notebook* on each person's
own computer, so that after the session each participant keeps a complete
copy of everything that was written.
Kokkoja runs in a web browser, beside whatever video call you are already
using. It replaces the screen sharing and annotation of a video call
application with something made for writing together, and it leaves the
voice and video to the call.
## What Kokkoja does
**A shared page, not a shared screen.** In a video call one person's
screen is transmitted as a picture, and annotations float on top of that
picture. In Kokkoja every participant's computer holds the page itself:
the image, if there is one, and every mark made on it. Only the changes
travel over the network. The marks belong to the page, are saved with it,
and can be shown or hidden later.
**Handwriting and typing.** Kokkoja is made for a pen on a drawing
tablet, with pressure giving the line its thickness, but a mouse or a
finger will do. Typed text is placed anywhere on the page and wraps at a
width you choose. Both come in any colour.
**Everyone writes on their own layer.** Each participant's writing is a
layer of its own. You can erase your own writing and nothing else; nobody
else can erase yours. Any layer can be hidden on your own screen without
affecting anyone else's view.
**A page that grows.** A page is an A4 sheet. When writing approaches an
edge, the sheet grows in that direction, so you are never short of room.
An image is placed at its natural size; one wider than the sheet is
scaled down to fit, and the sheet grows downward to hold further images.
**Saved without a save command.** The page you are on is written to your
notebook continuously, as it changes. Nothing is lost if a connection
drops or a computer crashes. When the session moves to another page, the
page being left is kept as a dated file. You can also keep a dated copy
of a page at any moment, and export a picture of it as a PNG file.
**Notebooks are yours.** A notebook is a directory on your own computer
and a page is a single file in it, an ordinary SVG file that any
image viewer or vector drawing program can open. Nothing is stored on any
server, and there are no accounts: you choose the name the others will
see, and that is all.
**Private.** Everything that travels between participants is encrypted
on their own computers. The relay server that passes the messages cannot
read them. The invitation to a session carries the key, so only those
who were invited can read the page.
**Nothing to install for guests.** A guest joins by opening a link in a
web browser. Someone who wants a notebook of real files on their own
computer installs the small Kokkoja program instead; that is also how a
host runs Kokkoja.
**Free software.** Kokkoja is free and open-source. Anyone can use it
without paying, and nobody depends on a company's service. It runs in
Firefox and in browsers built on the Chromium engine, such as Brave and
Chrome, on Linux and macOS.
## Who uses it
Kokkoja is a general tool for working on a page together at a distance:
going through a document with a colleague, sketching a diagram while
talking it over, correcting a draft, teaching anything that is done on
paper. Wherever two people would otherwise lean over one sheet, Kokkoja
puts the sheet between their computers.
Its ideal use, and the one it was made for first, is teaching a foreign
language over a video call. The teacher puts a scanned exercise on the
page. The student fills it in by hand or by typing, during the lesson,
while the teacher watches, corrects mistakes as they are made, and adds
notes in the margin. Each correction is in the teacher's own hand and
colour, on the teacher's own layer, where the student cannot change it.
When the lesson is over, the student's notebook holds every exercise of
the lesson, with the student's answers and the teacher's corrections
together on one page, and the teacher's notebook holds the same pages as
a record of what the student was given. The teacher's notebooks of
exercises become a library: loading a page from one of them puts a fresh
copy in front of a new student, and the original is never changed.
## Hosts and guests
A session has one *host*, the person who starts it, and any number of
*guests*. In a lesson the teacher is the host and the students are the
guests. The host sends the guests an invitation, by email, in a chat
message, or by any other means. The host *drives*: only the host puts
images on the page, starts new pages or loads pages from a notebook, and
everyone follows. The host can give any guest the same abilities, for a
discussion among colleagues.
The rest of this documentation describes Kokkoja from the guest's point of
view first, since the host sees the same screen and has a few more
buttons, then from the host's, and finally explains how it works
underneath, for those who want to run their own server or read the code.
* [A guest's Kokkoja](guest.md)
* [A host's Kokkoja](host.md)
* [How Kokkoja works](implementation.md)