docs: a guide, a wiki and screenshots

The README grew a section per feature and is a good reference, but nothing
said where to begin. docs/wiki now has a getting-started page, a guide that
walks through one day with the workspace and explains each part where it comes
up, and a page per part of the app, each with screenshots of what it
describes.

Three pages are read out of the code rather than written, so they cannot drift
from it: the keys and commands from Help.svelte and commands.ts, the same lists
`:help` draws; the settings from .env.example, which gains a heading for the
vault, trash and history settings so they no longer sit under mail; and the
API from the README's table. `make wiki` writes them, and `--check` fails when
one is stale or a link between pages leads nowhere. `--publish` lays the pages
out for a GitHub wiki: links without `.md`, images beside them, `[[` escaped so
the wiki does not take a command's help text for a link to a page.

The screenshots are taken by `make screenshots`: the debug binary on a
temporary data directory, with a password made up for the run, filled with an
example workspace over the API, and a headless Chromium driven over the
DevTools protocol through the views the docs show. Nothing outside the
temporary directory is touched but docs/images, and the pictures are cut to
256 colours, a third of their size, which a dark UI does not show.

The README opens with the banner, a CI badge, the guide, the features grouped
by what they are for, and six of the screenshots.
This commit is contained in:
nvrl committed 2026-10-21 12:07:32 +02:00
1 parent 7b2fd5579c
commit 4eb663e2d7
41 files changed
+2523 -25

No files matched your search

+64
View File
@@ -0,0 +1,64 @@
<!-- Generated by scripts/wiki.ts from .env.example. Edit that, then run `make wiki`. -->
# Configuration
Every setting is an environment variable, read at start, usually from a `.env`
file beside the binary (`cp .env.example .env`). The older `DIARY_<NAME>`
spelling of each is still accepted. A value that is set but cannot be read stops
the start with the reason rather than falling back to the default.
## Basics
| setting | example | what it does |
| --- | --- | --- |
| `WORKSPACE_USER` | `narl` | The one and only account. Both are checked in constant time on login. |
| `WORKSPACE_PASSWORD` | `change-me` | See the row above. |
| `WORKSPACE_SECRET` | `replace-with-32-random-characters` | Signs session cookies. Rotate it to log every device out. 32+ random chars. Generate one with: openssl rand -base64 32 |
| `WORKSPACE_DATA_DIR` | `./data` | Where the SQLite database and uploaded media live. |
| `WORKSPACE_BIND` | `127.0.0.1:4242` | Address to listen on. Put a TLS terminating reverse proxy in front of this. |
| `WORKSPACE_SESSION_DAYS` | `30` | How long a login lasts, in days. |
| `WORKSPACE_MAX_UPLOAD_MB` | `1024` | Largest upload, in megabytes: one request, or what an imported zip unpacks to. Uploads stream to disk, so this bounds the disk, not the memory. |
| `WORKSPACE_SECURE_COOKIE` | `0` | Set to 1 once the app is served over HTTPS: the cookie gets `Secure` and the `__Host-` prefix (everyone is logged out once), and browsers are told to use HTTPS only (HSTS). The server refuses to start with the example secret or password above still in place. |
## SFTP mount
| setting | example | what it does |
| --- | --- | --- |
| `WORKSPACE_SFTP_BIND` | `127.0.0.1:2222` | Off until this is set. Once it's up, register a key with `:sshkey add <name> <ssh-ed25519 ...>` in the app, then `sshfs -P <port> anything@host:/ mnt/` (the username is ignored — only the key decides who gets in). On the mount, `mkdir` at the root makes a space and anywhere else a folder; a new `.md` file is a new document. A leading `# Heading` is the document's title. Editor temp, swap and backup files are kept in memory for the session only, and renaming one onto a `.md` name saves it into that document. Under Docker, bind 0.0.0.0 (not 127.0.0.1) as the app listens inside the container — the host-side address in compose.yml's `ports:` is what actually restricts who can reach it. |
## Proton Drive backup
Off until `:backup login <username>` in the workspace (or `narl-workspace proton-login`) stores a session under WORKSPACE_DATA_DIR. These only shape how often the mirror runs.
| setting | example | what it does |
| --- | --- | --- |
| `WORKSPACE_PROTON_DEVICE` | `narl-workspace` | The device the server registers in Proton Drive; its sync root is the folder the diary is mirrored into. |
| `WORKSPACE_BACKUP_INTERVAL_MIN` | `60` | Backstop between runs, in minutes. 0 leaves it change-driven only. |
| `WORKSPACE_BACKUP_DEBOUNCE_SEC` | `300` | How long the diary must be quiet after a change before it is mirrored, so a writing session uploads once rather than after every save. |
| `WORKSPACE_BACKUP_PRUNE` | `0` | Set to 1 to let a deletion here trash the mirrored file there. Off by default: a backup that forgets on command is one accident from being none. |
| `WORKSPACE_SESSION_KEY_FILE` | `/run/secrets/session_key` | Seal the stored Proton session under a key kept off the data volume, so a copy of the volume alone carries no usable session. Point it at a Docker secret (see compose.override.yml.example); `openssl rand -base64 32` makes the key. Losing the key costs a new `proton-login`, nothing more. Unset by default. |
## Notification mail
Off until WORKSPACE_SMTP_URL is set. Nothing is even queued while it is unset.
| setting | example | what it does |
| --- | --- | --- |
| `WORKSPACE_SMTP_URL` | `smtps://mail.example.org:465` | The relay. `smtps://host:465` is implicit TLS, `smtp://host:587?tls=required` is STARTTLS, and a bare `smtp://host:1025` is plain — which only belongs on a local mail catcher. Credentials may live in the URL or in the two variables below. Unset by default. |
| `WORKSPACE_SMTP_USER` | | See the row above. Unset by default. |
| `WORKSPACE_SMTP_PASSWORD` | | See the row above. Unset by default. |
| `WORKSPACE_MAIL_FROM` | `workspace@example.org` | Where the mail comes from and goes to. FROM defaults to TO. Unset by default. |
| `WORKSPACE_MAIL_TO` | `you@example.org` | See the row above. Unset by default. |
| `WORKSPACE_TIMEZONE` | `Europe/Berlin` | "Did I write today?" and "is this due today?" are local-day questions, and the answer changes with the timezone rather than with a fixed offset. |
| `WORKSPACE_REMINDER_AT` | `20:00` | When to ask about the diary, if nothing was written in it that day. `off` turns it into no reminder at all. |
| `WORKSPACE_CARD_DUE_AT` | `08:00` | When to mail about cards that are due today or already overdue. |
| `WORKSPACE_DIGEST_AT` | `sun 18:00` | Weekday and time of the weekly digest. |
| `WORKSPACE_MAIL_KINDS` | `reminder,card_due,digest,login` | Which of the four kinds may be sent. Drop one from the list to switch it off. |
## Vault, trash and history
| setting | example | what it does |
| --- | --- | --- |
| `WORKSPACE_VAULT_IDLE_MIN` | `15` | Minutes the vault stays open with nothing using it, before it locks itself. It also locks on logout and whenever the server restarts. |
| `WORKSPACE_TRASH_DAYS` | `30` | Days a deleted document, folder, file or space waits in the trash, where :trash can bring it back, before it and its files are deleted for good. |
| `WORKSPACE_REVISION_DAYS` | `90` | Days a document's earlier versions are kept for :history. The newest 20 of every document are kept however old they are. |