From 997dffe01ce4db9e5e8e46a7588ce98694543e82 Mon Sep 17 00:00:00 2001 From: Kristian Wind Date: Wed, 19 Aug 2026 11:25:54 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20skriv=20det,=20der=20blev=20bygget,=20o?= =?UTF-8?q?g=20ret=20det,=20der=20ikke=20passer=20l=C3=A6ngere?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To nye sider. Postkasser: at stjerne i Gmail eller flage i iCloud og Fastmail bliver til opgaver, at hver postkasse hører til den, der forbandt den, at app-kodeord er noget andet end ens rigtige, og at Office 365 ikke kan nås ad den vej — sidstnævnte skrevet ned frem for at blive opdaget ved at prøve. Og at logge ind: kodeord, totrinskode og passkeys, med hvorfor en passkey ikke kan laves på en bar IP-adresse. Konfigurationen manglede VERDANDE_SECRET_KEY, som er den, det koster mest ikke at vide noget om: mister man nøglen, skal postkasserne forbindes forfra. Begrundelsen står med — det er kopierne, ikke serveren, der er problemet, for de kan hentes gennem fladen. Og genvejsafsnittet i quick-add beskrev q, t og u, som ikke findes længere. En dokumentation, der lover taster, der ikke gør noget, er værre end ingen: man prøver, der sker intet, og så tror man appen er i stykker. Bygget med --strict; alle krydshenvisninger rammer. Co-Authored-By: Claude Opus 5 --- docs/configuration.md | 38 ++++++++++++++++++- docs/mailboxes.md | 87 +++++++++++++++++++++++++++++++++++++++++++ docs/quick-add.md | 21 +++++++++-- docs/signing-in.md | 39 +++++++++++++++++++ mkdocs.yml | 2 + 5 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 docs/mailboxes.md create mode 100644 docs/signing-in.md diff --git a/docs/configuration.md b/docs/configuration.md index 8fb19b3..22241ad 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -55,6 +55,36 @@ One registration serves every user on the instance; each person authorises their own mailbox from **Settings → Gmail**. The redirect URI is derived from `VERDANDE_BASE_URL`, so it cannot drift out of step with what you registered. +## Secrets + +| Variable | Default | What it does | +|---|---|---| +| `VERDANDE_SECRET_KEY` | — | Encrypts the mail tokens and mailbox passwords in the database. 32 bytes of base64. | + +Mail tokens and [mailbox](mailboxes.md) passwords are encrypted at rest, and the +key does not live in the database. That is the whole point: **backups can be +downloaded through the interface**, so a copy that ends up somewhere it should +not would otherwise open your mail. + +Leave the variable empty and verdande writes a key to `secret.key` in the data +directory on first start, mode `0600`. Backups are a `VACUUM INTO` of the +database alone, so the key file is not in them. + +Generate one yourself if you would rather keep it out of the volume entirely: + +``` +openssl rand -base64 32 +``` + +!!! warning "Lose the key and the mailboxes must be reconnected" + Nothing else is lost — tasks, projects, files and history are not encrypted — + but a token that cannot be decrypted cannot be used, and there is no way back + from a key that is gone. If you restore a database somewhere new, bring + `secret.key` with it, or set `VERDANDE_SECRET_KEY` to the same value. + +A database written before there was a key is read as it stands and encrypted the +next time each value is written, so turning this on costs nobody a reconnection. + ## Update checks | Variable | Default | What it does | @@ -64,9 +94,13 @@ own mailbox from **Settings → Gmail**. The redirect URI is derived from | `VERDANDE_PANEL_TOKEN` | — | An API token from that panel, belonging to somebody with control of this server. | | `VERDANDE_PANEL_SERVER_ID` | — | Which server to restart — the id in the panel's URL. | -All three together let **Indstillinger → Notifikationer** restart this instance, +All three together let **Settings → Notifications** restart this instance, which is also how it picks up a new version: a container cannot replace its own -image, so the panel recreates it and pulls `:latest` on the way. Any one of the +image, so the panel recreates it and pulls `:latest` on the way. The request asks +the panel for a *scheduled* restart rather than an immediate one — an immediate +one is carried out while the request is still open, and stopping the container +kills the very process waiting for the answer, so the panel never gets to the +half that starts it again. Any one of the three alone does nothing, and the page names the ones that are missing. The token is a panel credential with control over that server. Anybody who can diff --git a/docs/mailboxes.md b/docs/mailboxes.md new file mode 100644 index 0000000..6e58eaf --- /dev/null +++ b/docs/mailboxes.md @@ -0,0 +1,87 @@ +# Mailboxes + +Star a mail and it becomes a task. + +This is the other direction from [mail to task](mail.md): instead of forwarding +something, Verdande reads a mailbox you connect and makes a task out of anything +you have flagged. Nothing else in the mailbox is touched, and nothing is ever +sent. + +Each mailbox belongs to the person who connected it. Two people on one instance +connect their own and see only their own — there is no shared inbox and no +administrator who can read yours. + +## Gmail + +**Settings → Integrations → Gmail → Connect.** + +Gmail needs an OAuth client, which is the instance's registration with Google +rather than anybody's account — see [Configuration](configuration.md#gmail). +Once it is set up, each person connects their own mailbox through it. + +Choose what makes a task: + +| Trigger | What it reads | +|---|---| +| Starred | Anything you have starred | +| Label | Anything carrying a label you name | + +!!! note "The app stays Internal if your client is Internal" + An OAuth client registered as *Internal* in a Google Workspace can only be + used by accounts in that workspace. A personal `@gmail.com` address will be + refused with `org_internal`, and that is the client working as configured. + Switching to *External* means refresh tokens that expire every seven days + until Google has verified the app — for one personal account, forwarding to + your [mail-to-task address](mail.md) is the shorter road. + +## iCloud, Fastmail and anything else over IMAP + +**Settings → Integrations → Mailboxes → Add a mailbox.** + +You need three things: + +| Field | Example | +|---|---| +| IMAP server | `imap.mail.me.com:993` | +| Username | your address | +| App password | not your ordinary password — see below | + +The connection is tested before it is saved. A mailbox that cannot be read is +refused while you are still looking at the form, rather than accepted and then +failing quietly every ten minutes. + +Flag a message — the flag, not unread — and it becomes a task on the next sweep. +Unread is a state you are still using yourself, and a sync that consumed it would +be taking something away. + +### App passwords + +Every host worth naming issues a separate password for applications, which you +can withdraw on its own without changing the one you sign in with. Use that. + +- **iCloud** — [appleid.apple.com](https://appleid.apple.com) → Sign-In and + Security → App-Specific Passwords +- **Fastmail** — Settings → Privacy & Security → Integrations → New app password + +The password is [encrypted at rest](configuration.md#secrets), and it is never +returned by the API once saved. + +### Office 365 does not work here + +Microsoft has closed IMAP to passwords; it wants its own OAuth flow. There is no +app password that will get you in, and a mailbox pointed at `outlook.office365.com` +will be refused. Forward to your [mail-to-task address](mail.md) instead. + +## How often + +Every ten minutes, in the background. **Fetch now** does it immediately, which is +for when you have just connected something and want to see it work. + +A run reads at most twenty-five messages, so a mailbox with years of flagged mail +in it fills up over several sweeps rather than in one. + +## Disconnecting + +**Disconnect** on the mailbox. The tasks it made stay — they are your work now, +not the mailbox's. Nothing is read after that, and the stored credential is +deleted rather than merely disabled. diff --git a/docs/quick-add.md b/docs/quick-add.md index a408dc5..a7cbbba 100644 --- a/docs/quick-add.md +++ b/docs/quick-add.md @@ -70,7 +70,22 @@ an error message and a lost thought. write it, and guessing month-first would silently move a date by months rather than failing visibly. -## The shortcuts +## Just start typing -Press ++q++ to jump to the quick-add box from anywhere, ++t++ for Today, ++u++ for -Upcoming, ++cmd+k++ to search, and ++question++ for the full list. +There is no shortcut to remember. Press a letter anywhere in the app and you are +writing a task, with the letter you pressed already in the field. The thought does +not have to survive a journey to find somewhere to put it. + +It only fires when nothing else has focus — a key pressed at a field, a button or +a link belongs to that thing. + +Under the field is a line saying what the parser reads: `#` project, `/` section, +`@` label, `p1` priority, and an example date. It appears while you are typing, +which is the one moment a placeholder cannot help you. + +++cmd+k++ opens the palette — search, and navigation to anywhere in the app. + +!!! note "The single letters used to navigate" + ++t++ for Today and ++u++ for Upcoming are gone. One keyboard cannot both + spell *tal med Anders* and jump to Today on the same ++t++, and capturing is + the thing done fifty times a day. The palette does the rest. diff --git a/docs/signing-in.md b/docs/signing-in.md new file mode 100644 index 0000000..893da4a --- /dev/null +++ b/docs/signing-in.md @@ -0,0 +1,39 @@ +# Signing in + +Three ways in, and you can have all of them at once. + +## A password + +What the first account is created with. Nothing is emailed, nothing is registered +anywhere: the first person to open a fresh instance creates the administrator +account, and after that people arrive by invite link. + +## A code on top of it + +**Settings → Account → Two-factor.** An ordinary TOTP secret — any authenticator +app reads the QR code. Once it is on, the password alone is not enough. + +Recovery codes are shown once, when you turn it on. They are the way back if the +phone is gone, and they are not shown again. + +## A passkey + +**Settings → Account → Passkeys → Add a passkey.** + +A key on a device instead of a secret in your head. The private half never leaves +the device — a phone's secure element, a laptop's keychain, a hardware key — and +the signature is bound to the address it was made for, so a convincing copy of the +sign-in page at a different address gets nothing. + +Give each one a name you will recognise later. *min bærbare* is what makes a list +of keys reviewable, and a list nobody can read is a list nobody revokes from. + +Passkeys need the address in `VERDANDE_BASE_URL` to be a hostname. Browsers refuse +to make one for a bare IP address, so an instance reached at `http://192.168.1.10:8080` +will show the passkey section as unavailable rather than failing at the moment +somebody tries. + +!!! note "One is enough, two is better" + Lose the only device holding your only passkey and you are back to the + password. Keep the password, or register a second key — a phone as well as a + laptop. diff --git a/mkdocs.yml b/mkdocs.yml index d575c7c..1260417 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -48,12 +48,14 @@ nav: - As a Rune: rune.md - Configuration: configuration.md - Using it: + - Signing in: signing-in.md - Quick add: quick-add.md - Filters: filters.md - Importing from Todoist: import.md - Connecting things: - Calendar and CalDAV: caldav.md - Mail to task: mail.md + - Mailboxes: mailboxes.md - Claude (MCP): mcp.md - AI features: ai.md # openapi.yaml is deliberately not in the nav: MkDocs copies it to the site as a