Moltis
Features

CalDAV (Calendars)

Moltis can read and manage remote calendars through the CalDAV protocol. Once configured, the agent gains a caldav tool that can list calendars, query events, and create, update, or delete entries on your behalf.

Moltis also provides a read-only CalDAV connector that synchronizes remote calendar data into local datasets. The connector is configured from Settings -> Connectors and is independent from the agent tool described below. The same connector subsystem can also reuse supported channel accounts for bounded message-history datasets; see Connectors.

Both capabilities are compiled in by default. The connectors cargo feature controls inbound synchronization, while caldav controls the agent tool. They can be omitted from custom builds with --no-default-features.

Although the live tool and local connector have separate execution paths, their authentication can be shared. On startup, every valid account under [caldav.accounts] is automatically created or reconciled as a connector connection and appears in Settings -> Connectors. Fastmail and iCloud URLs use the same provider defaults as the live tool. For a generic provider, the configured URL must be the actual CalDAV context endpoint: the connector does not yet follow the live tool’s broader service-discovery path.

Imported connections are identified by their config account key, so restarts do not create duplicates. A single matching connection that was previously created in Settings is adopted instead. Changes to the configured URL, username, password, timeout, or global caldav.enabled state are reconciled; unchanged encrypted passwords are preserved byte-for-byte. Connector-only network permissions remain editable in Settings and are not overwritten by config reconciliation. Removing or invalidating a config entry disables its imported connection and clears the copied credential, but never deletes its local datasets, history, or projections. Restoring a valid entry restores the credential and re-enables the same connection.

Config-managed connections are labeled in Settings. Their identity, credentials, timeout, enabled state, and removal are controlled by moltis.toml; only their advanced network permissions can be changed in the connector UI. This restriction is enforced by the connector API as well as the UI. Manually created connections require the password to be re-entered whenever the server URL or username changes, preventing a stored credential from being silently forwarded to a different authority.

When an initialized vault is sealed, imports and password rotations that would write a secret are deferred rather than stored as plaintext. Reconciliation is retried automatically after the vault is unsealed.

Inbound connector

Use the inbound connector when you want a durable local mirror that agents can search without waiting for the CalDAV server:

  1. Open Settings -> Connectors -> Connections.

  2. Add a CalDAV connection with its HTTPS server URL, username, and an app password, or use an account imported automatically from [caldav.accounts].

  3. Open Datasets and describe what Moltis should keep locally, for example:

    Keep events from the past year where I accepted the invitation.
    Refresh every 30 minutes and generate Markdown projections.
  4. Select Compile plan. The configured model translates the instruction into a typed plan containing calendar scope, absolute date bounds, participation filtering, schedule, and projection settings.

  5. Review the compiled summary and save the dataset.

Changing the instruction invalidates the previous plan until it is compiled again. An advanced JSON override is available for inspecting or precisely adjusting the generated plan, but free-form model output is never executed directly.

Plan compilation sends the instruction and discovered calendar display names to the first configured tool-capable model, falling back to the first configured model. Private CalDAV hrefs are replaced by opaque identifiers before this request. Moltis does not send the password, username, server URL, dataset/account IDs, or event bodies. Compilation requires a configured model and is separate from scheduled synchronization. Configure only models whose privacy policy is appropriate for calendar names and the instructions you enter.

The connector stores canonical records in connectors.db. Each dataset keeps calendar metadata and raw iCalendar resources, including recurrence rules, exceptions, time zone definitions, alarms, and custom properties. Recurrences are preserved rather than expanded into an unbounded list of occurrences.

On later runs, Moltis compares CalDAV ETags and fetches only new or changed resources. Source state is retained for both included and filtered resources, so an unchanged rejected event is not repeatedly downloaded. Changing the compiled plan increments its revision and forces affected resources to be evaluated again. Missing resources are marked deleted only after a complete, successful calendar inventory. A failed or interrupted sync therefore cannot erase the last valid local snapshot.

CalDAV date filtering currently compares each VEVENT component’s DTSTART date. The start bound is inclusive and the end bound is exclusive. Recurrence rules are preserved but not expanded, so a recurring series is evaluated by its stored components rather than every generated occurrence. Relative phrases such as “the past year” are resolved to absolute dates when the plan is compiled.

“Accepted by me” is evaluated locally by matching the account username against an ATTENDEE value with PARTSTAT=ACCEPTED. Servers generally cannot apply that predicate themselves.

Generated projections are written beneath:

<data_dir>/connectors/exports/<dataset-name>-<dataset-id-hash>/

The exact path appears on the dataset card after projections are enabled. manifest.json describes the snapshot, items.jsonl supports scripts and static dashboards, and items/*.md contains generic Markdown records.

Connector passwords are stored as secrets and are encrypted through the Vault when encryption at rest is enabled and unlocked. API and UI responses never return the password. Calendar contents remain sensitive local data in connectors.db and projection files, so protect the Moltis data directory and exported files accordingly.

HTTPS and public network targets are required by default. Advanced settings can permit cleartext HTTP or private-network servers for explicitly trusted local CalDAV installations. These options weaken the default network protections and should only be enabled when required. Connector server URLs cannot contain embedded credentials, query strings, or fragments; authentication belongs in the dedicated username and password fields. Public hosts are resolved once, every returned address is validated, and those addresses are pinned for the CalDAV client so DNS rebinding cannot switch a validated host to a private address during connection.

Local agent access

Trusted agent turns receive a read-only connectors tool. It operates entirely on connectors.db; it never contacts the CalDAV server. The available operations are:

  • list_datasets — list synchronized datasets and freshness state.
  • search_items — bounded full-text search over one local dataset.
  • get_item — retrieve one selected local item.

Search results are intentionally small previews, while get_item returns a bounded full body. Results are marked untrusted_external because calendar content can contain prompt injection and must be treated as data, not instructions. The tool does not expose connector accounts, credentials, absolute projection paths, synchronization controls, or mutation operations. It is trusted-only by default and can be removed further through normal agent tool policy.

The inbound connector intentionally does not expand recurring events, write events, or use CalDAV push notifications. The separate live caldav tool can still create, update, and delete events, while normal reads can use the faster local connectors tool.

Configuration

Add a [caldav] section to your moltis.toml (usually ~/.moltis/moltis.toml):

[caldav]
enabled = true
default_account = "fastmail"

[caldav.accounts.fastmail]
provider = "fastmail"
username = "you@fastmail.com"
password = "app-specific-password"

Multiple accounts

You can define as many accounts as you like. When only one account exists it is used implicitly; otherwise specify default_account or pass account in each tool call.

[caldav]
enabled = true
default_account = "work"

[caldav.accounts.work]
provider = "fastmail"
username = "work@fastmail.com"
password = "app-specific-password"

[caldav.accounts.personal]
provider = "icloud"
username = "you@icloud.com"
password = "app-specific-password"

Supported providers

Providerprovider valueNotes
Fastmail"fastmail"URL auto-discovered (caldav.fastmail.com). Use an app password.
iCloud"icloud"URL auto-discovered (caldav.icloud.com). Requires an app-specific password.
Generic"generic"Any CalDAV server. You must set url.

For generic servers, provide the CalDAV base URL:

[caldav.accounts.nextcloud]
provider = "generic"
url      = "https://cloud.example.com/remote.php/dav"
username = "admin"
password = "secret"

Account fields

FieldRequiredDefaultDescription
providerno"generic"Provider hint ("fastmail", "icloud", "generic")
urldependsCalDAV base URL. Required for generic; optional for Fastmail/iCloud (well-known URL used).
usernameyesAuthentication username
passwordyesPassword or app-specific password
timeout_secondsno30HTTP request timeout

How it works

When Moltis starts and CalDAV is enabled with at least one account, a caldav tool is registered in the agent tool registry. The agent can then call it during conversations to interact with your calendars.

Connections are established lazily on first use and cached for the lifetime of the process. All communication uses HTTPS with system-native TLS roots.

Operations

The agent calls the caldav tool with an operation parameter. Five operations are available:

list_calendars

Lists all calendars available on the account.

Returns: href, display_name, color, description for each calendar.

list_events

Lists events in a specific calendar, optionally filtered by date range. When both start and end are given, the filter runs server-side as a CalDAV calendar-query REPORT with a time-range element (RFC 4791), so only events intersecting the window are fetched. Recurring resources are expanded by the server for that window, so results contain the matching occurrences rather than an out-of-range recurrence master. If either bound is omitted, all events are returned without recurrence expansion.

ParameterRequiredDescription
calendaryesCalendar href (from list_calendars)
startnoISO 8601 start date/time (naive times are treated as UTC)
endnoISO 8601 end date/time (naive times are treated as UTC)

Returns: href, etag, uid, summary, start, end, all_day, location for each event.

create_event

Creates a new calendar event.

ParameterRequiredDescription
calendaryesCalendar href
summaryyesEvent title
startyesISO 8601 start (e.g. 2025-06-15T10:00:00 or 2025-06-15 for all-day)
endnoISO 8601 end date/time
all_daynoBoolean, default false
locationnoEvent location
descriptionnoEvent notes

Returns: href, etag, uid of the created event.

update_event

Updates an existing event. Uses ETag-based optimistic concurrency control to prevent overwriting concurrent changes.

ParameterRequiredDescription
event_hrefyesEvent href (from list_events)
etagyesCurrent ETag (from list_events)
summarynoNew title
startnoNew start
endnoNew end
all_daynoNew all-day flag
locationnoNew location
descriptionnoNew description

Returns: updated href and etag.

delete_event

Deletes an event. Also requires the current ETag.

ParameterRequiredDescription
event_hrefyesEvent href
etagyesCurrent ETag

Concurrency control

Updates and deletes require an etag obtained from list_events. If the event was modified on the server since the ETag was fetched (e.g. edited from a phone), the server rejects the request with a conflict error. This prevents accidental overwrites. The agent should re-fetch the event and retry.

Example conversation

You: What’s on my calendar this week?

The agent calls list_calendars, picks the primary calendar, then calls list_events with start / end spanning the current week.

Agent: You have 3 events this week: …

You: Move the dentist appointment to Friday at 2pm.

The agent calls update_event with the event’s href and etag, setting the new start time.

Disabling CalDAV

Set enabled = false or remove the [caldav] section entirely:

[caldav]
enabled = false

To disable at compile time, build without the feature:

cargo build --release --no-default-features --features lightweight

Validation

moltis config check validates CalDAV configuration and warns about unknown providers. Valid provider values are: fastmail, icloud, generic.