Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ It sits behind your single sign-on (such as [Authelia](https://www.authelia.com/
- Undo send for a few seconds after sending
- Recipient suggestions from your [Davis](https://github.com/tchapi/davis) address books (_optional_)

### Contacts (_optional, with Davis_)

- Your address books, in the same three panes as mail: browse, filter, pick which book you look at
- Create, edit and delete contacts, with undo. What other apps wrote on a card and Postel does not show is left as it is
- Pictures, with a crop
- Download a contact, an address book or all of them as `.vcf`, and import `.vcf` files
- Possible duplicates pointed out on a card, merged in two clicks
- Senders you know show their name and picture from their card, and open it; the others can be added from the message

### Interface

- Light and dark mode, nine accent colours, two densities
Expand Down Expand Up @@ -61,7 +70,7 @@ Comes as one Docker image (php-fpm and Caddy) for `amd64` and `arm64`, with a sa
- An IMAP server that supports master users and the `SORT`, `THREAD`, `MOVE` and `PREVIEW` extensions. [Dovecot](https://www.dovecot.org/) does, and it is the only one tested (_2.3.19, as shipped with [docker-mailserver](https://docker-mailserver.github.io/docker-mailserver/latest/) 15_)
- An SMTP submission server that accepts the same master user. With Dovecot as the SASL backend for Postfix, which is the usual setup, it does
- A reverse proxy doing forward-auth, which sets `Remote-User`, `Remote-Email` and `Remote-Name` on each request. For instance Traefik or Caddy with Authelia
- [Davis](https://github.com/tchapi/davis) 5.6 or later, if you want recipient suggestions (_optional_)
- [Davis](https://github.com/tchapi/davis) 5.6 or later, if you want contacts and recipient suggestions (_optional_)

To run it without Docker, you also need PHP 8.4 (with the `intl` extension), Composer 2 and Node 22 or later to build the frontend.

Expand All @@ -74,7 +83,7 @@ Whatever the way, you need to:
1. Create a [Dovecot master user](#dovecot-master-user) for Postel
2. Put Postel behind a [reverse proxy with forward-auth](#-reverse-proxy-and-authentication)
3. Set the [configuration](#configuration)
4. Optionally, create a [Davis app key](#davis-app-key) for recipient suggestions
4. Optionally, create a [Davis app key](#davis-app-key) for contacts and recipient suggestions

## Configuration

Expand All @@ -95,7 +104,7 @@ Everything is set with environment variables.
| `SMTP_PORT` | `587` | Submission port |
| `SMTP_ENCRYPTION` | `starttls` | `false`, `ssl` or `tls` (_implicit TLS, usually port 465_), or `starttls` (_usually port 587_) |
| `SMTP_VALIDATE_CERT` | `true` | Check the SMTP server's certificate |
| `DAVIS_URL` | | The DAV root of your Davis server, for instance `https://dav.example.com/dav/`. Leave it empty to turn recipient suggestions off |
| `DAVIS_URL` | | The DAV root of your Davis server, for instance `https://dav.example.com/dav/`. Leave it empty to turn contacts and recipient suggestions off |
| `DAVIS_APP_ID` | | The Davis app key's id (`app_…`) |
| `DAVIS_APP_SECRET` | | The Davis app key's secret |
| `DAVIS_LOGIN_ATTRIBUTE` | `username` | Which header is the Davis login: `username` (`Remote-User`) or `email` (`Remote-Email`) |
Expand Down Expand Up @@ -127,7 +136,7 @@ The `<user>` part comes from the forward-auth headers: the email (`Remote-Email`

## Davis app key

Recipient suggestions come from the user's [Davis](https://github.com/tchapi/davis) address books, read with an app key: Postel logs in as `<user>*<app id>` with the key's secret, and Davis acts as `<user>`. It needs Davis 5.6 or later.
Contacts and recipient suggestions come from the user's [Davis](https://github.com/tchapi/davis) address books, read and written with an app key: Postel logs in as `<user>*<app id>` with the key's secret, and Davis acts as `<user>`. It needs Davis 5.6 or later.

1. Turn app keys on in Davis with `APP_KEYS_ENABLED=true`
2. Create a key, for all users or only the ones using Postel:
Expand All @@ -144,11 +153,15 @@ _The **App keys** page of the Davis dashboard does the same._

> [!NOTE]
>
> Davis never creates an account for an app key: each user needs an existing Davis account with the same name. Users without one simply get no suggestions.
> Davis never creates an account for an app key: each user needs an existing Davis account with the same name. Users without one get an empty address book and no suggestions.
>
> The key lets Postel change the address books too: contacts created, edited or deleted in Postel are written to Davis.
>
> `--all-users` does not include Davis admins. If an admin uses Postel, add them with `--user`.

Postel keeps a copy of the names and addresses in its SQLite file, and checks Davis for changes at most every five minutes. Suggestions never wait for Davis.
Postel keeps a copy of the cards in its SQLite file, so the address book opens at once and suggestions never wait for Davis. It checks Davis for changes when you open your contacts, and otherwise at most every five minutes. A change made in Postel goes to Davis first, and is refused rather than written over a change made elsewhere in the meantime.

Postel only speaks standard CardDAV to Davis. The app key is the one thing that needs Davis: it is what lets Postel act as the user without their password.

# 🌐 Reverse proxy and authentication

Expand Down
4 changes: 4 additions & 0 deletions backend/.env.test
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ SMTP_HOST=127.0.0.1
SMTP_PORT=1587
SMTP_ENCRYPTION=false
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data/test.db"
# A fake address book server answers, see tests/Contacts/FakeCardDav.php
DAVIS_URL=https://dav.example.test/dav/
DAVIS_APP_ID=app_test
DAVIS_APP_SECRET=secret
1 change: 1 addition & 0 deletions backend/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"doctrine/orm": "^3.7",
"phpdocumentor/type-resolver": "^2.1",
"phpstan/phpdoc-parser": "^2.3",
"sabre/vobject": "^5.0",
"symfony/console": "7.4.*",
"symfony/dotenv": "7.4.*",
"symfony/flex": "^2",
Expand Down
245 changes: 244 additions & 1 deletion backend/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions backend/config/packages/framework.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ framework:
when@test:
framework:
test: true
# The address book server is a fake one in tests, see tests/Contacts/FakeCardDav.php
http_client:
mock_response_factory: App\Tests\Contacts\FakeCardDav
5 changes: 5 additions & 0 deletions backend/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ services:
# please note that last definitions always *replace* previous ones

ZBateson\MailMimeParser\MailMimeParser: ~

when@test:
services:
App\Tests\Contacts\FakeCardDav:
public: true
Loading
Loading