# EDS auto-registration

> **Status:** candidate format. The `.source` keys and the Secret Service attribute schema below are based on the public Evolution Data Server documentation and on inspection of representative installs, **but they have not been verified against a real EDS install on Pedro's system yet**. Marked `TODO( verify )` items must be cross-checked before Phase 5 ships.

## Goal

At first launch, `contacts-server` makes the CardDAV endpoint discoverable by Evolution Data Server (and through it GNOME Contacts, Evolution, Geary) without any manual user action. Concretely:

1. Write a `.source` file describing the address book.
2. Store the Basic-auth token in Secret Service under the attributes EDS expects to look it up by.
3. On every restart, refresh the `.source` if the CardDAV port changed.
4. Skip the whole step if EDS is not installed.

KDE / Akonadi has no equivalent auto-registration in v1 — users add the URL manually. README documents this.

## Detection: is EDS present?

`contacts-server` checks for the existence of the directory:

```
$XDG_CONFIG_HOME/evolution/sources/
```

(typically `~/.config/evolution/sources/`).

If absent, log at `info` level (`"EDS not detected; skipping address book auto-registration"`) and continue. Do not error, do not warn — EDS is optional.

If present, proceed. We do not separately check for `evolution-source-registry` running; writing a fresh `.source` causes `evolution-source-registry` to pick it up on its next inotify event or on next start.

## `.source` file

Path:

```
$XDG_CONFIG_HOME/evolution/sources/liberux-contacts.source
```

The basename without the `.source` suffix becomes the source UID — `liberux-contacts`. We pick a stable, namespaced UID rather than a random UUID so the file is recognizable by humans and so re-writes target the same source.

### Format

EDS source files are GKeyFile (INI-like). The candidate content is:

```ini
[Data Source]
DisplayName=Liberux Contacts
Enabled=true
Parent=liberux-contacts-collection

[Authentication]
Host=127.0.0.1
Port=<dynamic>
ProxyUid=system-proxy
RememberPassword=true
User=liberux
Method=plain/password

[Security]
Method=none

[WebDAV Backend]
ResourcePath=/addressbook/main/
EmailAddress=
DisplayName=Liberux Contacts
Order=0

[Offline]
StaySynchronized=true
IncludeMe=true

[Address Book]
BackendName=carddav
Order=0
```

And a sibling collection source so EDS treats the address book as part of a logical "Liberux" account:

Path: `$XDG_CONFIG_HOME/evolution/sources/liberux-contacts-collection.source`

```ini
[Data Source]
DisplayName=Liberux
Enabled=true

[Authentication]
Host=127.0.0.1
Port=<dynamic>
ProxyUid=system-proxy
RememberPassword=true
User=liberux

[Collection]
BackendName=webdav
Identity=liberux@local
ContactsEnabled=true
CalendarEnabled=false
MailEnabled=false
```

`<dynamic>` is the actual ephemeral port the CardDAV server bound to during this startup.

> `TODO( verify )`: keys/values to confirm against a fresh EDS install with a working CardDAV source attached:
> - `[Address Book] BackendName` — is it `carddav` (newer EDS) or `webdav` (older)? They differ between EDS releases.
> - Whether `[WebDAV Backend] ResourcePath` is the right group/key name vs. `[CardDAV Backend]`.
> - Whether `Parent=` is required for non-collection sources.
> - The exact `Method=plain/password` token (could be `Method=plain/password` vs. `Method=none` with credentials elsewhere).
> - Whether `[Security] Method=none` is the right way to declare HTTP-not-HTTPS.

The verification procedure (Pedro to run on a system with EDS):

```
gnome-control-center online-accounts   # or evolution itself
# add a CardDAV source pointing at any test server
cat ~/.config/evolution/sources/<generated-uid>.source
```

The keys listed in that file are authoritative.

### Re-write logic

On every startup:

1. Compute the desired contents from the freshly-bound port and the static fields above.
2. If `liberux-contacts.source` does not exist, write it (`mode 0644`).
3. If it exists, parse it. Compare the `[Authentication] Port=` (and any field that depends on port). If anything differs from desired, overwrite the file atomically (`write to .source.tmp` + `rename`).
4. If it exists and matches, leave it alone — touching the mtime would cause `evolution-source-registry` to reload unnecessarily.

Same logic for `liberux-contacts-collection.source`.

### Disabling auto-registration

For users who want to manage the source themselves, `contacts-server` reads:

```
$XDG_CONFIG_HOME/liberux/contacts/no-eds-autoregister
```

If this file exists (any contents), the EDS step is skipped entirely, and any prior `.source` files are left untouched. Documented in README.

## Secret Service: storing the token

EDS uses libsecret to look up CardDAV passwords. The schema name and the lookup attributes have been verified against EDS 3.5x by inspecting the entry EDS itself created when the user typed the password the first time.

### Schema

| Attribute key   | Value                                                                                                              |
| --------------- | ------------------------------------------------------------------------------------------------------------------ |
| `e-source-uid`  | `liberux-contacts-collection` — the **parent collection** UID, not the address book. EDS resolves credentials at the collection level for any nested source (as set by `Parent=…` in `liberux-contacts.source`). |

The libsecret schema name to register the secret under: `org.gnome.Evolution.Data.Source` (note the `.Data.`).

The label shown in seahorse / similar UIs: `"Liberux Contacts password"`.

We deliberately do not set an `eds-origin` attribute. EDS sets it to `evolution-data-server` on the entries it creates; matching that value would be misleading, and using a different value would create a parallel item that EDS lookups wouldn't filter against. Without the attribute our item still matches EDS's lookup (which only uses `e-source-uid` + the schema-derived `xdg:schema`).

### Writing the token

`contacts-server` writes the token at first launch:

```rust
secret_service.store(
    schema = "org.gnome.Evolution.Data.Source",
    attributes = {
        "e-source-uid": "liberux-contacts-collection",
    },
    label = "Liberux Contacts password",
    secret = <token bytes>,
    replace_existing = true,
);
```

(Pseudocode — actual call uses a Secret Service zbus client. We do not depend on `libsecret` directly; we speak the `org.freedesktop.secrets` D-Bus interface.)

If the store fails (e.g. no Secret Service provider running), `contacts-server`:

- Logs at `warn`.
- Falls back to writing the token in `[Authentication] Password=` of the `.source` file with mode `0600` and `Method=plain/password`. EDS accepts this for offline / no-keyring environments. README documents the fallback.

> `TODO( verify )`: confirm EDS reads `[Authentication] Password=` as a password fallback. If not, the only fallback is to require Secret Service.

### Reading the token in subsequent runs

The token is generated **once** and persisted:

1. Primary store: Secret Service (above).
2. Local cache: `$XDG_CONFIG_HOME/liberux/contacts/token` (mode `0600`).

Both must agree; if they disagree (e.g. user wiped the keyring), `contacts-server` regenerates a token, updates Secret Service, updates the local file, updates the `.source`, and bumps an internal "credentials version" so EDS re-prompts (or, with `RememberPassword=true`, silently re-fetches).

The local cache exists so that a `contacts-server` restart with the keyring locked (e.g. early boot) can still serve CardDAV — without the cache, the daemon would hang waiting for a keyring unlock to learn its own password.

## Runtime port discovery file

`$XDG_RUNTIME_DIR/liberux/contacts.port`

Single line, ASCII decimal, mode `0600`, ending in `\n`. Written after the listener binds and rewritten on every startup.

This file is for ad-hoc tooling and for the rare consumer that cannot parse the `.source`. EDS itself does not read it — EDS gets the port from the `.source` keys.

The directory `$XDG_RUNTIME_DIR/liberux/` is created with mode `0700` if missing.

## Integration test plan

To be exercised manually before declaring Phase 5 done:

1. Fresh user, no `.source` files, EDS installed.
   - Start `contacts-server`.
   - Verify `~/.config/evolution/sources/liberux-contacts.source` and `liberux-contacts-collection.source` appear with the actual port.
   - Verify `secret-tool search e-source-uid liberux-contacts` returns the token.
   - Verify `gnome-contacts` shows the Liberux address book.
2. Restart `contacts-server` (port likely changes).
   - Verify the `.source` Port= is updated; everything else is left alone.
   - Verify `gnome-contacts` continues to work without re-prompting.
3. Wipe the keyring, restart.
   - Verify `contacts-server` re-stores the token from its local cache without regenerating it.
4. Create the `no-eds-autoregister` opt-out file, restart.
   - Verify the `.source` files are not touched.
5. Uninstall EDS, restart.
   - Verify the `info`-level skip log message; no error; CardDAV still works for KDE/manual users.
