Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Verified Commit 7ecc375d authored by Romain Hunault's avatar Romain Hunault 🚴🏻
Browse files

docs(workspace): define backend-aware Workspace integration contract

Adds package.md (Dokka convention) documenting the authoritative
cross-app contract: stable descriptor fields, discovery rules for
mail autoconfig and OIDC, internal implementation details, and
compatibility expectations.

Closes #117
parent a7866380
Loading
Loading
Loading
Loading
Loading
+104 −0
Original line number Diff line number Diff line
# Package at.bitfire.davdroid.workspace

Murena Workspace backend integration contract.

## Overview

This package defines the authoritative contract for backend-aware Murena Workspace integration
across /e/OS apps. It provides a structured description of a workspace instance (the *descriptor*)
and the rules for discovering dynamic configuration (OIDC issuer, mail autoconfig).

All public APIs in this package are considered stable client-facing contract unless explicitly
marked otherwise.

---

## Backend descriptor: `MurenaWorkspaceDescriptor`

`MurenaWorkspaceDescriptor` is the central data class. It holds all endpoints needed to integrate
with a Murena Workspace instance. Downstream apps **must** obtain a descriptor via
[at.bitfire.davdroid.util.MurenaServerConfig.getDescriptor] rather than constructing one directly.

### Stable contract fields

These fields are derived deterministically from `workspaceDomain` and are safe to persist or
cache across sessions:

| Field | Example value | Derivation rule |
|---|---|---|
| `baseWebUrl` | `https://murena.io` | `https://{workspaceDomain}` |
| `dashboardUrl` | `https://murena.io/apps/dashboard` | `{baseWebUrl}/apps/dashboard` |
| `davBaseUrl` | `https://murena.io/remote.php/dav` | `{baseWebUrl}/remote.php/dav` |
| `mailAutoconfigUrl` | `https://autoconfig.murena.io/mail/config-v1.1.xml` | `https://autoconfig.{workspaceDomain}/mail/config-v1.1.xml` |
| `mailAutoconfigWellKnownUrl` | `https://murena.io/.well-known/autoconfig/mail/config-v1.1.xml` | `{baseWebUrl}/.well-known/autoconfig/mail/config-v1.1.xml` (RFC 5785) |

### Dynamic fields (null until discovery)

These fields require a network discovery step before use:

| Field | Populated by | Null meaning |
|---|---|---|
| `oidcIssuer` | `MurenaOidcDiscovery.discover` | Discovery has not run yet, or OIDC is not configured |
| `oidcDiscoveryUrl` | derived from `oidcIssuer` | Same as above |

Callers **must not** assume `oidcIssuer` is non-null without having run discovery first.

### `Branding` (informational)

`MurenaWorkspaceDescriptor.Branding` is purely informational and not required for any
integration flow. Its values may change without notice and must not be used as identifiers.

---

## Discovery rules

### Mail autoconfig

Mail clients should attempt the two endpoints in order:

1. **Primary**`mailAutoconfigUrl` (`https://autoconfig.{domain}/mail/config-v1.1.xml`)
2. **Fallback**`mailAutoconfigWellKnownUrl` (`{baseWebUrl}/.well-known/autoconfig/mail/config-v1.1.xml`, RFC 5785)

Both endpoints serve the Thunderbird autoconfig XML format (version 1.1).

### OIDC issuer discovery (`MurenaOidcDiscovery`)

The OIDC issuer cannot be derived statically from the domain because the Keycloak realm name
varies per deployment. `MurenaOidcDiscovery.discover` resolves it by following the
unauthenticated redirect from the Nextcloud `oidc_login` endpoint:

```
GET https://{workspaceDomain}/apps/oidc_login/oidc
  → 302 https://accounts.example.com/auth/realms/{realm}/protocol/openid-connect/auth?...
```

The issuer is extracted by stripping the `/protocol/openid-connect` suffix from the redirect
location. The discovery result is one of three sealed outcomes:

- `Result.Discovered` — OIDC is configured; the returned descriptor is enriched with `oidcIssuer`.
- `Result.NotConfigured` — OIDC is absent or the redirect target is not an OIDC endpoint.
- `Result.Failed` — A transient network or server error prevented discovery.

Callers should treat `Result.NotConfigured` as a permanent signal for the current server
configuration, and `Result.Failed` as a transient condition eligible for retry.

---

## Internal implementation details

The following are **not** part of the downstream contract and may change without notice:

- The HTTP redirect-following strategy in `MurenaOidcDiscovery` (currently single-hop, no-redirect client).
- The exact path segment used to detect Keycloak endpoints (`/protocol/openid-connect`).
- The `MurenaOidcDiscovery.extractIssuer` function (package-internal).

---

## Compatibility expectations

- `workspaceDomain` is the only required constructor parameter; all other fields are derived or optional.
- The URL derivation rules (scheme, paths, subdomains) are stable and will not change without a
  major version bump of this module.
- Fields introduced in future versions will default to null or a safe default to remain
  backward-compatible with existing descriptors.
- `MurenaWorkspaceDescriptor` is a `data class`: equality and copy are part of the contract.