[JMAP] Re: JMAP for Calendars comments

Neil Jenkins <neilj@fastmailteam.com> Wed, 08 October 2025 10:20 UTC

Return-Path: <neilj@fastmailteam.com>
X-Original-To: jmap@mail2.ietf.org
Delivered-To: jmap@mail2.ietf.org
Received: from localhost (localhost [127.0.0.1]) by mail2.ietf.org (Postfix) with ESMTP id 35D6D6F43A0C for <jmap@mail2.ietf.org>; Wed, 8 Oct 2025 03:20:27 -0700 (PDT)
X-Virus-Scanned: amavisd-new at ietf.org
X-Spam-Flag: NO
X-Spam-Score: -2.798
X-Spam-Level:
X-Spam-Status: No, score=-2.798 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_PASS=-0.001] autolearn=ham autolearn_force=no
Authentication-Results: mail2.ietf.org (amavisd-new); dkim=pass (2048-bit key) header.d=fastmailteam.com header.b="SC6En+u0"; dkim=pass (2048-bit key) header.d=messagingengine.com header.b="eh2EEXFk"
Received: from mail2.ietf.org ([166.84.6.31]) by localhost (mail2.ietf.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id GjAwjp4vJSr4 for <jmap@mail2.ietf.org>; Wed, 8 Oct 2025 03:20:26 -0700 (PDT)
Received: from fout-b6-smtp.messagingengine.com (fout-b6-smtp.messagingengine.com [202.12.124.149]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-256)) (No client certificate requested) by mail2.ietf.org (Postfix) with ESMTPS id 46ACB6F439F3 for <jmap@ietf.org>; Wed, 8 Oct 2025 03:20:26 -0700 (PDT)
Received: from phl-compute-02.internal (phl-compute-02.internal [10.202.2.42]) by mailfout.stl.internal (Postfix) with ESMTP id F0ADB1D005F2 for <jmap@ietf.org>; Wed, 8 Oct 2025 06:20:25 -0400 (EDT)
Received: from phl-imap-15 ([10.202.2.104]) by phl-compute-02.internal (MEProxy); Wed, 08 Oct 2025 06:20:26 -0400
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= fastmailteam.com; h=cc:content-type:content-type:date:date:from :from:in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1759918825; x= 1760005225; bh=wn5SMpe940qAVzbFk4WY1RParZgc+OpeH0qG3SoDvWA=; b=S C6En+u0Vyu59/70YWPfOEhB/Bre8eQCKPRBli4ID0aK/sDUAzskLfZkzA6c34DBC AKQNz9HxUnghMdku+RBqDzMA6YWmGl9etIMErei9oVxw/n7M5yPQNOAx+KoyNy8w 84mSK8cY22TQIq9zy+LuU2LsS2jWyGwqFlNAC+Lc3wlXF3Q52QOVmPw8AtX4jwEn 0QYUv4axquVZMfki7M8TSmr2g/6j9+P370xCmFe9rEh09RnPNlu4kgRoj5W7zt0I Q89dHynW9YcKPC5o9vreXzDMUzKFn3oJpk0dKbpwUBznkRnAY7tmrr8H0b58I2Sg ZNC8CTWNTp/96OnTHXwkQ==
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm2; t= 1759918825; x=1760005225; bh=wn5SMpe940qAVzbFk4WY1RParZgc+OpeH0q G3SoDvWA=; b=eh2EEXFkDTl67VzOvQTjfyNFvJ+rXQR7VvwdLE0w5ro3NL/rJ9N grDmg4jWEfLCZj5lLb4MH0v50+0nW/vsGZv5xOfTI5g7aeDWTk+AeW2UO0ys4g5E ZRclsqTGwFBpZjohC1CPBalexQVJfZR5nLs5HtbdprvTmE5LD15bxa41DXupV/21 yvCfmrpS/nJ3v4tCpbHNvuhNUyPyZfMUjWxJwYGRLMG05gpwXPY+GHv1Gj5p+6AI zhRTZnzwCTkzA6/FiRA1FN8DjpRVJgJHNMGUICtsMIhPqwBm5I/TUPilrAxPe9l3 JqRPPrmeLsYpEK2MchkEeQAmFHm0wPwVswg==
X-ME-Sender: <xms:6TrmaENeDIwcPeOrNYc8-QfBAokZ_7sf2C9Vh9TySiBAqKtDejsh6g> <xme:6TrmaFwoR5njrjBorwm21YrCCIi0Rq4pr0m2gNuwARyDh1NK2MwZIr4uEO_tPCtap rQ0K9iNK00jNyCWI8yfqCZHcnY3VpCac8ajq0nbRG8KHwat8i8>
X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddutdeftdegucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhepofggfffhvffkjghfufgtsegrtderreertdejnecuhfhrohhmpedfpfgvihhlucfl vghnkhhinhhsfdcuoehnvghilhhjsehfrghsthhmrghilhhtvggrmhdrtghomheqnecugg ftrfgrthhtvghrnhepjefggfeileehgffgfedtffefvefgteejleeutefgieelledvtdeu keejieffvdeinecuffhomhgrihhnpehjohhhnhdrughopdgvgigrmhhplhgvrdgtohhmpd hivghtfhdrohhrghenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhl fhhrohhmpehnvghilhhjsehfrghsthhmrghilhhtvggrmhdrtghomhdpnhgspghrtghpth htohepuddpmhhouggvpehsmhhtphhouhhtpdhrtghpthhtohepjhhmrghpsehivghtfhdr ohhrgh
X-ME-Proxy: <xmx:6TrmaLyOMOWgEcQ2j4uf6dgoj2bnFrvGhkqhFdbSpmjH2wepZZnltA> <xmx:6TrmaKPC2XlCXYXkoXMHgRPB3Fo6RIPQy1pdDVQCSP4YaWZUvDSEDQ> <xmx:6TrmaK-8_MrV92tH_rTUpGzdiaApRYR8iIgA7ExuXkUO7zU_ZEXoqA> <xmx:6TrmaKoR65rvUL0vE4mDbrhUM7MHKdhddObHNUGZFhOLFaUxvgvsww> <xmx:6TrmaEIyYkWbTafWd1RtI9vO-OrqVdy857flmnke6Qj0FrlMOA_lmaBL>
Feedback-ID: ibc614277:Fastmail
Received: by mailuser.phl.internal (Postfix, from userid 501) id 7B79178012B; Wed, 8 Oct 2025 06:20:25 -0400 (EDT)
X-Mailer: MessagingEngine.com Webmail Interface
MIME-Version: 1.0
X-ThreadId: An5MunO63raJ
Date: Wed, 08 Oct 2025 21:19:14 +1100
From: Neil Jenkins <neilj@fastmailteam.com>
To: IETF JMAP Mailing List <jmap@ietf.org>
Message-Id: <4aa2017d-5ef4-486f-a264-2c8e2bec6094@dogfoodapp.fastmail.com>
In-Reply-To: <101DEC25-A12F-4063-90FB-446C6E5190BF@stalw.art>
References: <101DEC25-A12F-4063-90FB-446C6E5190BF@stalw.art>
Content-Type: multipart/alternative; boundary="59edcad764214417a0ab4f2886ffb0ec"
Message-ID-Hash: VJIZGZDXTUGA33GOA2HPHS4CNE5W7ZUI
X-Message-ID-Hash: VJIZGZDXTUGA33GOA2HPHS4CNE5W7ZUI
X-MailFrom: neilj@fastmailteam.com
X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-jmap.ietf.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header
X-Mailman-Version: 3.3.9rc6
Precedence: list
Subject: [JMAP] Re: JMAP for Calendars comments
List-Id: JSON Meta Access Protocol <jmap.ietf.org>
Archived-At: <https://mailarchive.ietf.org/arch/msg/jmap/sO5-7SLkB6TNOzYIFsYK4uDxpas>
List-Archive: <https://mailarchive.ietf.org/arch/browse/jmap>
List-Help: <mailto:jmap-request@ietf.org?subject=help>
List-Owner: <mailto:jmap-owner@ietf.org>
List-Post: <mailto:jmap@ietf.org>
List-Subscribe: <mailto:jmap-join@ietf.org>
List-Unsubscribe: <mailto:jmap-leave@ietf.org>

Hi Mauro,

Thanks for all the great feedback!

> First, a few typos and editorial things I spotted:
> - Section 4: “initally” should be “initially”.
> - Section 1.5.1: “can be can assigned” -> “can be assigned”.
> - Section 5.1: it says “four new JSCalendar properties” but only three are defined (mayInviteSelf, mayInviteOthers, hideAttendees).

Thanks, fixed.

> - Section 5.6.1: the sentence “Splitting an event however is problematic…” may read better as “However, splitting an event is problematic…”.

I had some other feedback about the clarity of this, so I've rewritten it to hopefully be easier to follow.

> - Section 2.2 (Principal/getAvailability): Returning the full/partial event in the availability response could make responses unnecessarily large (even when using ranges). In addition to this, clients may already have the event cached. It might be better to return only the synthetic ids of the events and let clients fetch the details with a CalendarEvent/get call using a result reference to list/*/eventIds in the Principal/getAvailability response.

I think this would considerably complicate things, and I'm not convinced the saving is that necessary. The user may only have permission to call Principal/getAvailability and no other access to the data in the account, so this method wouldn't always work, so we'd still need everything we currently have in the spec. Generally the time period involved will be short (e.g. no more than a week), so the volume of data is unlikely to be that high, and the client requests which properties it wants (the `eventProperties` argument) and even whether it wants the details at all (the `showDetails` argument), so has a lot of control in ensuring the volume of data is reasonable.

> - Section 3 (ParticipantIdentity): It’s not clear whether participant identities are associated with each calendar or if they’re account-level objects like email identities. If they are calendar-specific, which permissions apply when viewing or modifying them?

To confirm, as you later clarified, these are account-level objects, like email identities.

> What I was trying to ask is how this works when a user has access to shared accounts. For example, let’s say a user with accountId A1 has access to a shared account A2, and that shared account contains a calendar. Are the ParticipantIdentity objects defined in the user’s account (A1), or in the account where the calendar lives (A2)?

They are defined where the calendar lives (A2). Note though, different sharees of A2 may have access to the same calendars but get different ParticipantIdentity objects. Consider:
 • If A2 is the account of Ms Boss, and it's shared with Mr Secretary, then the ParticipantIdentities will be the same as the owner of A2 (the sharee is acting as the owner, aka secretary mode).
 • But if A2 is an account representing Team Alpha, the calendar may represent the team but each sharee acts as themselves on it, so the ParticipantIdentities will be the same as in A1 (the sharee is acting as themself).
The server doesn't have to implement both modes of course, but the flexibility is there in the API.

> After re-reading Section 3, I now understand that ParticipantIdentity objects are per-account. If that’s the case, would it perhaps be clearer to expose them by extending the Principal object with a new property, similar to how CalDAV handles this with the calendar-user-address-set property?

I don't think putting this as a property on the Principal object makes sense. The user might fetch the Principal object for everyone in their company, but they only need the participant identities for the accounts that are shared with them. Furthermore, some people consider their list of alternate email addresses to be private information. The current approach also is more consistent with mail, and allows individual identities to be created/updated/destroyed rather than having to update a single compound property on an object.

> For example, in Stalwart, calendar addresses are derived from the account’s email address(es) and cannot be modified by users. If we allow users to add other participant identities that are not their email addresses, how should the server validate that they are authorized to use those identities?

Just to confirm, as you later realised this yourself, the sever can always reject any create with a standard `forbidden` SetError, just like with email identities. What you choose to allow is up to you and your policies.

> As for permissions, my understanding is that ParticipantIdentity objects can only be retrieved and modified by the account owner (otherwise the spec would need to define how to set permissions for them).

Not quite. They can be retrieved by the sharee as well (see above). In theory you could allow them to `/set` as well, but I imagine most servers would forbid this, as the ParticipantIdentity objects seen by the user would be determined by a combination of server policy, configuration (e.g. are calendars shared in secretary or team mode), and what calendars have been shared with the user.

> My confusion mainly comes from how calendar addresses are handled in CalDAV. So let me ask a related question: how can one calendar user discover the principal ID or account ID associated with a given (local) calendar address? If the calendar address is simply an email listed under a Principal object, that’s straightforward. But consider this example:
> 
> - John’s Principal lists john@example.com as the email address.
> - John also has two ParticipantIdentity objects: mailto:john@example.com and mailto:john.doe@example.com.
> 
> Now, suppose Jane sees mailto:john.doe@example.com listed as a participant in an event and wants to query John’s availability using Principal/getAvailability. How can Jane determine which principal ID is associated with that address, given that it’s defined as a ParticipantIdentity under John’s account? The Principal/query method does not currently allow querying by calendar address and Principal objects can contain just one email, so how should a client resolve that mapping?

Interesting, I see where you're coming from now with all this. A few thoughts:
 • It would have to be a policy decision for the server over whether users are allowed to resolve the owner of the alias; as mentioned earlier, some people consider them private.
 • The most common scenario (by far) is for the *organiser* to be checking availability, and they would not have this problem (because you go the other way → find the principals you want to schedule with, check their availability, then get the address to invite from the Principal).
 • If we did want to support this, my feeling is the best way would be to add an optional `principalId` property to the Participant <https://www.ietf.org/archive/id/draft-ietf-calext-jscalendarbis-09.html#name-participants> object, which the server would automatically add if it could resolve which Principal this participant corresponded to, and its policy gave user permission to see this. Do others think this is worth adding?

> - Default Alerts: It might be worth clarifying that only OffsetTrigger alerts should be used in set requests. AbsoluteTrigger does not make sense for a default alert.

Yeah, you'd have thought this would be obvious but doesn't hurt to add a MUST NOT on this I think. I'll do that.

> - Permissions (Section 4): I think mayRSVP should be renamed to mayUpdateRSVP, since it refers to the permission to update an RSVP status rather than the permission to RSVP.

Disagree, I think `mayRSVP` is more accurate. RSVP is also a verb, and the permission is about the ability to RSVP, which depending on the event permissions may involve adding yourself as a participant to the event object, not just setting the RSVP property.

> - Section 5.11.1 (Filtering): Some filters accept null as an argument. In some cases this makes sense (to indicate absence of a value), but for others like text, before, or after it doesn’t really make sense.

Yeah, this is inconsistent with JMAP Mail + JMAP Contacts. I've updated it to be consistent with those. Instead of being nullable, I've noted each property is optional.

> - iCalComponent: Since the iCalComponent property may include unnecessary converted data (like VTIMEZONE components) that most JMAP Calendar clients won’t care about, maybe it’s worth suggesting that such components and properties should not be returned by default unless explicitly requested.

Yeah, agreed; I've added this.

> - Calendar deletion: I think the spec should specify whether scheduling messages are sent when an entire calendar is deleted.

This is already covered in the description of Calendar/set <https://www.ietf.org/archive/id/draft-ietf-jmap-calendars-24.html#section-4.3> (emphasis added below):

*onDestroyRemoveEvents*: Boolean (default: false)
If false, any attempt to destroy a Calendar that still has CalendarEvents in it will be rejected with a calendarHasEvent SetError. If true, any CalendarEvents that were in the Calendar will be removed from it, and if in no other Calendars they will be destroyed. 
*This MUST NOT send scheduling messages to participants.*

> Personally, I don’t think sending them is necessary, but it might be useful to have an option similar to sendSchedulingMessages in Calendar/set, just for consistency.

I don't think it's necessary either:
 • Sending a huge batch of cancellations is almost never what you want to do.
 • It's likely to hit rate limits due to the number being sent at once with a large calendar, and so some may fail, and if that happens do you still continue with the calendar deletion?
 • Clients that really want to do this can always manually destroy the events first with `CalendarEvent/set` to send the scheduling messages.

> - Section 5.3 – Attachments: The blobId property mentioned there is not currently registered with IANA.

Good spot, I've added a registration.

I've uploaded a v25 draft with the changes.

Cheers,
Neil.