Return-Path: <rsto@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 BCCAC13324384;
	Tue,  1 Sep 2026 08:07:48 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ietf.org; s=ietf1;
	t=1788275268; bh=V+zZSF8KicQqGA6vjhCJ7HBGavWaWnbrEk4/sKWceUU=;
	h=Date:From:To:Subject;
	b=Hjb1ObkjwT7lheKwG89xn3/ZHtwJ15qxPy1hbnTl7mguQnd5pLa4rg8QYlk1vHqbt
	 OL09C2ORO8mz9g3drbxC5IQMEK91uUbkxr0yl3wCST3OLb2/VzkL3qJnYlN4ML/ItM
	 aueWYlhW1mvE5uCE/s4HUC5oNJBCSYNVfbpG5Toc=
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="ctiowNbp";
	dkim=pass (2048-bit key) header.d=messagingengine.com
	header.b="d9UvZevC"
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 iqjo3KOtnFXR; Tue,  1 Sep 2026 08:07:47 -0700 (PDT)
Received: from fout-a6-smtp.messagingengine.com
 (fout-a6-smtp.messagingengine.com [103.168.172.149])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange X25519 server-signature ECDSA (P-256) server-digest SHA256)
	(No client certificate requested)
	by mail2.ietf.org (Postfix) with ESMTPS id C616C1332437A;
	Tue,  1 Sep 2026 08:07:47 -0700 (PDT)
Received: from phl-compute-10.internal (phl-compute-10.internal [10.202.2.50])
	by mailfout.phl.internal (Postfix) with ESMTP id D9CD6EC0116;
	Tue,  1 Sep 2026 11:07:41 -0400 (EDT)
Received: from phl-imap-15 ([10.202.2.104])
  by phl-compute-10.internal (MEProxy); Tue, 01 Sep 2026 11:07:41 -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:message-id:mime-version:reply-to:subject
	:subject:to:to; s=fm1; t=1788275261; x=1788361661; bh=lmr7W4+rxj
	qkyUlRKL6TuGInMAauOsDNqY+mWlrE+EY=; b=ctiowNbpHxqzz9DhaCl9ZEBD0/
	FAIkTumMnmquTQJ9jAcIyR9f1cYuGEHisqCpmbTh+SmydIlMSEQAxB19A+lEwR7k
	+Pw6oppZK5v7y5hBrr53xxduHqw/y4cjXdF7u4UX7OHuFhtIgw/HjsPlTrHZMZgj
	Tmn7t6ouOMshSSuwiKGn8Vc9pk+Sdci6nmAxZFcKbJM+jAP3BFCUufSTRDlTBJFT
	8kMWhgjFJWcMLLQPEdhblAZ7HIYMTEXHzmtyjSp8yIdyU5TtpXRIZry69HvEyive
	DAhFkFEV2IG/mEzfwIwgbDYJbjWsnF8Msb6xnBiJ8hHHYVsm2k8cPBMvPC2g==
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:message-id
	:mime-version:reply-to:subject:subject:to:to:x-me-proxy
	:x-me-sender:x-me-sender:x-sasl-enc; s=fm3; t=1788275261; x=
	1788361661; bh=lmr7W4+rxjqkyUlRKL6TuGInMAauOsDNqY+mWlrE+EY=; b=d
	9UvZevCaykg5bJIdiecnRYDUDUVcbzjmaOeYxOUDEBTxbKNScYsMrBAFB3j0zSyy
	RxSUSNSK0kXRw7u79J+oeFa2eENwR7UXnbIVHmxMh8g0P4LJrQKZsiPOiCERtf9+
	cxtjpyjldZxFPdmk/nsdoyi9HHuTpeib8Ni+lyBLLBtwCxIeHpzjCda3iYkT0J9q
	NRmYqJ8CjivoljCIcm1ktX4LGd1qmGZDKTcjslX1kj3rOfYqaT5uqmc5zXIuwoZg
	KeHbfvWz3vSpFR9vJp9TgixkdIf0KxcFDwyrZeP5xgAaa0ph05HPBT9lQUVqzR+T
	wkrqbp6sGeOx2gxdgZ70A==
X-ME-Sender: <xms:PeqWaspRGr0pwoLD9LAiXIif1GtLgm-H4hfM1VefTP2cbAmsTPp_hw>
    <xme:PeqWatcid5fsgIrSLEJbyNuSDyijzqiXIucTvRVWIpx6EjNjYDgdK3pndmiG1YAyv
    _-jOzVbEgjVRnaBFjy6pDAdsbbsqv6daUKhB-5i5IIL04AmJEQ>
X-ME-Proxy-Cause: 
 dmFkZTE4E+GReBquc6twB2WQwFwApvTHjdzhxfeff+mwDR47DjJDKlKmdc1CAIPWonv2V+
    c8oA0bhNZAqFtdtFshsvX72HaUun2CiCgL+jjITx9+qQYthYbMUfQXPfqtzlkjmkzcX7Fe
    IfEeyKNiIg3gISicpxnKoDi8XtzAKtLN6QEd4TXtK986TjxI3rDjyHqwe/u9XpJw8QhJZv
    ZZ2fIzwZC/AuQ69S8Fh7lZ+9/YUfrRYKR1gADW8Do88/Vlm3sA6uxgragoGJSNTRxa4o1z
    9psG87DVvnLzQMOv8AYRttQubl2OgtVCdAfpIANKqbHZHxldpLKP+2rKhGIW5awvfMbCcn
    nRWfCBVC3YnE0Oba6MfWQZwRHpN4xefHzwp9y9f6+8JYSqdzMZw1sCBIKfxQqoaai9PABJ
    PTpRx6G/2aq0xzRXCilWfQepsCx8Ek3OZH+1lThrd//blYxhlVlI4Ory6UBNosthJYgxNR
    qlqYBlZzVSWoQTY8Ql3PFM5jInLep+V2fCBJlkLZjpsm9MqGVw6NmlSjTfYTeRtu26JKdL
    u6DAvlQPNXZckRqKd9MqjFEzgTYfIdg9bUVDjnrnTUTz+V8nD7EG72EbQEmjBjL3zgbsqW
    fy+dPSkp0STLH64VZ2YtFeyZ+2cw/wZmueUnQ/l9whGf7Rnk/nexSEMs/YfA
X-ME-Proxy: <xmx:PeqWahJJdExK9I5G-B6o7JazsB2OaBEflW-sWUDqgudjPuHwAV6JUw>
    <xmx:PeqWajLoVNjiok3XKiSac6kirBfwA4NwUKKE0YoHPijVW4zCNUkIOw>
    <xmx:PeqWanW_JAP_Ab4KD0-J0-iUKIuNHHB8wsrXDW4MNDb9Iblfv3ax5Q>
    <xmx:PeqWatjrXngJcljS53momtlOJQr4pGct216c46KLg9nJuTbQdLUNRA>
    <xmx:PeqWakkfoCIxnVt9oF8kDtBamOcz5ezJJV4v5-qYp6b1FWfB8JkUI3pN>
Feedback-ID: ia5d944da:Fastmail
Received: by mailuser.phl.internal (Postfix, from userid 501)
	id A33347811F0; Tue,  1 Sep 2026 11:07:41 -0400 (EDT)
X-Mailer: MessagingEngine.com Webmail Interface
MIME-Version: 1.0
Date: Tue, 01 Sep 2026 17:06:58 +0200
From: "Robert Stepanek" <rsto@fastmailteam.com>
To: calsify@ietf.org, jmap@ietf.org
Message-Id: <7a865d64-677c-414c-b6ab-2dfaaa5b7d1c@app.fastmail.com>
Content-Type: multipart/alternative;
 boundary=566c0d4646d1c9c6388758e55c3595de1210794e
Message-ID-Hash: QJI2RZQMQNIREPC74MEXXNOATJ53666F
X-Message-ID-Hash: QJI2RZQMQNIREPC74MEXXNOATJ53666F
X-MailFrom: rsto@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: =?utf-8?q?=5BJMAP=5D_JMAP_CalendarEvent_privacy_quirks?=
List-Id: JSON Meta Access Protocol <jmap.ietf.org>
Archived-At: 
 <https://mailarchive.ietf.org/arch/msg/jmap/JCckaRP0nLdC2CSJN5unGOJEylc>
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>

--566c0d4646d1c9c6388758e55c3595de1210794e
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

*Cross-posting to calext and jmap; please keep replies on the calext lis=
t.*

We are reimplementing support for private calendar events in Cyrus IMAP =
and encountered quirks with the `privacy` property. I would like to disc=
uss them now, rather than after publication. I understand that Last Call=
 for the relevant documents ends later this week. To allow for discussio=
n, I have asked the chairs to extend Last Call at least for a couple of =
days.

This email is long, but I want us to make an informed decision. Also, th=
e formatting and length of this email might tempt you to think that this=
 was produced by an LLM. It is not, I edited this on HedgeDocs and struc=
tured it for readability. In Section 1, I briefly describe my core assum=
ptions, Sections 2 and 3 describe the quirks with private events I encou=
ntered, and Section 4 then outlines my proposal.

TLDR, I am proposing three changes:
 1. For JSCalendar, we forbid patching `privacy` in a recurrence overrid=
e, rather than ignoring it.
 2. For JMAP Calendars, we require the same privacy level for all Calend=
arEvents sharing a `uid` within an account.
 3. For iCalendar conversion, we redefine that the most restrictive `CLA=
SS` property of a main component and its recurrence overrides converts t=
o JSCalendar privacy.
1. Introduction

For the following, I will assume that anyone implementing JMAP Calendars=
 and CalDAV will want to enforce privacy consistently in both protocols =
(rather than differently or not at all for CalDAV). Even if that=E2=80=99=
s not the case, you might still want to understand the quirks I outline =
below, as some apply to JMAP Calendars regardless.

Also, privacy is only enforced for shared calendars within one calendar =
service. For iMIP, any privacy level provided by the sender is only a su=
ggestion: a recipient may choose to ignore it and iTIP payloads always c=
ontain full event details, even for privacy levels =E2=80=9Cprivate=E2=80=
=9D or =E2=80=9Csecret=E2=80=9D.

Lastly, from here on I use =E2=80=9Cprivate event=E2=80=9D (without quot=
es) to mean an event whose privacy level is either =E2=80=9Cprivate=E2=80=
=9D or =E2=80=9Csecret=E2=80=9D. For the relevant definitions see jscale=
ndarbis-18 <https://www.ietf.org/archive/id/draft-ietf-calext-jscalendar=
bis-18.html#name-privacy>, jmap-calendars <https://www.ietf.org/archive/=
id/draft-ietf-jmap-calendars-28.html> and jscalendar-icalendar <https://=
www.ietf.org/archive/id/draft-ietf-calext-jscalendar-icalendar-25.html#n=
ame-class>.
2. Quirks with non-recurring private events

Non-recurring private events mostly are straightforward to implement con=
sistently for both JMAP and CalDAV: a calendar owner can read and write =
events of all privacy levels. A calendar sharee can read and write =E2=80=
=9Cpublic=E2=80=9D events in full, can read only the basic time and meta=
data of =E2=80=9Cprivate=E2=80=9D events but cannot write them, and a sh=
aree cannot see or otherwise access =E2=80=9Csecret=E2=80=9D events at a=
ll. Except for the following quirk:

*Quirk#1*: The existence of a =E2=80=9Csecret=E2=80=9D event can leak to=
 sharees with write access. A CalDAV client on a shared calendar may PUT=
, COPY or MOVE a calendar event where the UID or CalDAV resource name ma=
tches that of a =E2=80=9Csecret=E2=80=9D event. In this case, the server=
 has to reject the request with a `CALDAV:no-uid-conflict` precondition,=
 which reveals that a resource with that name or UID exists. Likewise, a=
 JMAP Calendars server must reject a `CalendarEvent/set` method if it wo=
uld create an already existing secret `uid` in that account. The server =
can return a `forbidden` or `invalidProperties` SetError, but either lea=
ks existence of that secret `uid`.

I see this first quirk as inevitable. If leaking the existence of a secr=
et event identifier is a concern, then the service must prevent secret e=
vents from ever becoming non-secret, whether by forbidding scheduling th=
ese events or by forbidding an event that has ever been non-secret from =
becoming secret. In any case, even that does not prevent a client from e=
numerating random (or not so random) identifiers.
3. Quirks with recurring private events

For recurring private events things become more complicated. Currently, =
JSCalendar defines that patching the `privacy` property in a recurrence =
override must be ignored. For example, a client may attempt to create th=
e following calendar event with a "private" override, but in fact all oc=
currences of the event will be =E2=80=9Cpublic=E2=80=9D:

{
  "privacy": "public",
  "recurrenceOverrides": { "2026-08-28T01:00:00" : { "privacy": "private=
" } }
  ...
}

*Quirk#2*: For `CalendarEvent/get`, an unaware developer might be led to=
 assume by the above snippet that overriding `privacy` is possible. They=
 may retrieve such an event either because another client created it, or=
 the event got converted from iCalendar where CLASS may differ between t=
he main event and its override exceptions.

*Quirk#3*: For `CalendarEvent/set`, it is underspecified what =E2=80=9Ci=
gnoring the privacy patch=E2=80=9D means. A server that interprets it to=
 remove privacy from the patch object can indicate that to a client in t=
he `CalendarEvent/set` response fields. A server that ignores but preser=
ves the patch does not, and will return it verbatim in a later `/get`.

*Quirk#4*: For `CalendarEvent/set`, whether a `privacy` patch takes effe=
ct depends on what the event id refers to. For a synthetic id retrieved =
from a recurring event when expanding recurrences in `/query`, the serve=
r must process the patch as an update to a recurrence override of the ba=
se event, and `privacy` is ignored. If the id denotes a stand-alone recu=
rrence instance, `privacy` is a top-level property and the patch takes e=
ffect. Clients can only tell the two apart by the `baseEventId` property.

*Quirk#5*: Related to the previous quirk, two stand-alone recurrence ins=
tances may have differing privacy levels and both take effect, but only =
as long as no main event for these instances exists. As soon as the main=
 event exists, their previous privacy levels become ineffective and are =
defined by the main event.

*Quirk#6*: For CalDAV interoperability, a server might either keep on ac=
cepting but ignoring differing CLASS property values in override excepti=
ons, or it has to rewrite the CLASS properties of these overrides during=
 PUT. For the latter, it might also need to rewrite its existing calenda=
r resources and instruct CalDAV clients to GET the rewritten data by bum=
ping the ETag.
4. Proposal

To address all but the last quirk of recurring events, I propose the fol=
lowing changes:
 =E2=80=A2 In JSCalendar version =E2=80=9C2.0=E2=80=9D and later, we for=
bid patching `privacy` in a recurrence override, rather than ignoring it=
. JMAP Calendars implementations that keep on supporting version =E2=80=9C=
1.0=E2=80=9D should accept but strip `privacy` patches in recurrenceOver=
rides and should not return such patches in `CalendarEvent/get`.
 =E2=80=A2 For JMAP Calendars, we require the same privacy level for all=
 calendar objects having the same `uid`. A `CalendarEvent/set` that, aft=
er all changes in the `/set` have been applied, causes the privacy of a =
given `uid` to become ambiguous within an account must be rejected. For =
scheduling invites, an updated event keeps whatever privacy was assigned=
 to an existing event with that `uid`.
 =E2=80=A2 When converting from iCalendar to JSCalendar, the most restri=
ctive CLASS value of a main component and all its recurrence overrides c=
onverts to the privacy of the converted Event or Task. For stand-alone r=
ecurrence instances with the same UID, the most restrictive of their CLA=
SS values converts to the privacy of each converted object.
This leaves quirk #6, for I see no way around it, regardless if we keep =
the existing `privacy` definitions or update them as I propose. The core=
 issue is that CalDAV does not define anything about the CLASS property =
and its iCalendar definition is very vague, too. In our implementation, =
we will aim to rewrite newly created and existing iCalendar data to matc=
h JMAP Calendars and JSCalendar semantics as much as possible.

Regards,
Robert
--566c0d4646d1c9c6388758e55c3595de1210794e
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE html><html><head><title></title></head><body><div><i>Cross-pos=
ting to calext and jmap; please keep replies on the calext list.</i></di=
v><div><br></div><div>We are reimplementing support for private calendar=
 events in Cyrus IMAP and encountered quirks with the <code style=3D"bor=
der-top-width:1px;border-top-style:solid;border-top-color:rgb(204, 204, =
204);border-right-width:1px;border-right-style:solid;border-right-color:=
rgb(204, 204, 204);border-bottom-width:1px;border-bottom-style:solid;bor=
der-bottom-color:rgb(204, 204, 204);border-left-width:1px;border-left-st=
yle:solid;border-left-color:rgb(204, 204, 204);border-image-outset:0;bor=
der-image-repeat:stretch;border-image-slice:100%;border-image-source:non=
e;border-image-width:1;border-top-left-radius:3px;border-top-right-radiu=
s:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;backg=
round-color:rgb(246, 246, 246);background-position-x:0%;background-posit=
ion-y:0%;background-repeat:repeat;background-attachment:scroll;backgroun=
d-image:none;background-size:auto;background-origin:padding-box;backgrou=
nd-clip:border-box;font-family:menlo, consolas, monospace;font-size:90%;=
padding-top:1px;padding-right:3px;padding-bottom:1px;padding-left:3px;">=
privacy</code> property. I would like to discuss them now, rather than a=
fter publication. I=0A understand that Last Call for the relevant docume=
nts ends later this=20=0Aweek. To allow for discussion, I have asked the=
 chairs to extend Last=20=0ACall at least for a couple of days.</div><di=
v><br></div><div>This=0A email is long, but I want us to make an informe=
d decision. Also, the formatting and length of this email might tempt yo=
u to think that this was produced by an LLM. It is not, I edited this on=
 HedgeDocs and structured it for readability. In Section=20=0A1, I brief=
ly describe my core assumptions, Sections 2 and 3 describe the=20=0Aquir=
ks with private events I encountered, and Section 4 then outlines my=0A =
proposal.</div><div><br></div><div>TLDR, I am proposing three changes:</=
div><ol><li>For JSCalendar, we forbid patching <code style=3D"border-top=
-width:1px;border-top-style:solid;border-top-color:rgb(204, 204, 204);bo=
rder-right-width:1px;border-right-style:solid;border-right-color:rgb(204=
, 204, 204);border-bottom-width:1px;border-bottom-style:solid;border-bot=
tom-color:rgb(204, 204, 204);border-left-width:1px;border-left-style:sol=
id;border-left-color:rgb(204, 204, 204);border-image-outset:0;border-ima=
ge-repeat:stretch;border-image-slice:100%;border-image-source:none;borde=
r-image-width:1;border-top-left-radius:3px;border-top-right-radius:3px;b=
order-bottom-right-radius:3px;border-bottom-left-radius:3px;background-c=
olor:rgb(246, 246, 246);background-position-x:0%;background-position-y:0=
%;background-repeat:repeat;background-attachment:scroll;background-image=
:none;background-size:auto;background-origin:padding-box;background-clip=
:border-box;font-family:menlo, consolas, monospace;font-size:90%;padding=
-top:1px;padding-right:3px;padding-bottom:1px;padding-left:3px;">privacy=
</code> in a recurrence override, rather than ignoring it.</li><li>For J=
MAP Calendars, we require the same privacy level for all CalendarEvents =
sharing a <code style=3D"border-top-width:1px;border-top-style:solid;bor=
der-top-color:rgb(204, 204, 204);border-right-width:1px;border-right-sty=
le:solid;border-right-color:rgb(204, 204, 204);border-bottom-width:1px;b=
order-bottom-style:solid;border-bottom-color:rgb(204, 204, 204);border-l=
eft-width:1px;border-left-style:solid;border-left-color:rgb(204, 204, 20=
4);border-image-outset:0;border-image-repeat:stretch;border-image-slice:=
100%;border-image-source:none;border-image-width:1;border-top-left-radiu=
s:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-=
bottom-left-radius:3px;background-color:rgb(246, 246, 246);background-po=
sition-x:0%;background-position-y:0%;background-repeat:repeat;background=
-attachment:scroll;background-image:none;background-size:auto;background=
-origin:padding-box;background-clip:border-box;font-family:menlo, consol=
as, monospace;font-size:90%;padding-top:1px;padding-right:3px;padding-bo=
ttom:1px;padding-left:3px;">uid</code> within an account.</li><li>For iC=
alendar conversion, we redefine that the most restrictive <code style=3D=
"border-top-width:1px;border-top-style:solid;border-top-color:rgb(204, 2=
04, 204);border-right-width:1px;border-right-style:solid;border-right-co=
lor:rgb(204, 204, 204);border-bottom-width:1px;border-bottom-style:solid=
;border-bottom-color:rgb(204, 204, 204);border-left-width:1px;border-lef=
t-style:solid;border-left-color:rgb(204, 204, 204);border-image-outset:0=
;border-image-repeat:stretch;border-image-slice:100%;border-image-source=
:none;border-image-width:1;border-top-left-radius:3px;border-top-right-r=
adius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;b=
ackground-color:rgb(246, 246, 246);background-position-x:0%;background-p=
osition-y:0%;background-repeat:repeat;background-attachment:scroll;backg=
round-image:none;background-size:auto;background-origin:padding-box;back=
ground-clip:border-box;font-family:menlo, consolas, monospace;font-size:=
90%;padding-top:1px;padding-right:3px;padding-bottom:1px;padding-left:3p=
x;">CLASS</code> property of a main component and its recurrence overrid=
es converts to JSCalendar privacy.</li></ol><h2>1. Introduction</h2><div=
>For=0A the following, I will assume that anyone implementing JMAP Calen=
dars=20=0Aand CalDAV will want to enforce privacy consistently in both p=
rotocols=20=0A(rather than differently or not at all for CalDAV). Even i=
f that=E2=80=99s not=20=0Athe case, you might still want to understand t=
he quirks I outline below,=0A as some apply to JMAP Calendars regardless=
.</div><div><br></div><div>Also, privacy is only enforced for shared cal=
endars within one=20=0Acalendar service. For iMIP, any privacy level pro=
vided by the sender is=20=0Aonly a suggestion: a recipient may choose to=
 ignore it and iTIP payloads=0A always contain full event details, even =
for privacy levels =E2=80=9Cprivate=E2=80=9D or=0A =E2=80=9Csecret=E2=80=
=9D.</div><div><br></div><div>Lastly,=0A from here on I use =E2=80=9Cpri=
vate event=E2=80=9D (without quotes) to mean an event=20=0Awhose privacy=
 level is either =E2=80=9Cprivate=E2=80=9D or =E2=80=9Csecret=E2=80=9D. =
For the relevant definitions see&nbsp;<a href=3D"https://www.ietf.org/ar=
chive/id/draft-ietf-calext-jscalendarbis-18.html#name-privacy">jscalenda=
rbis-18</a>, <a href=3D"https://www.ietf.org/archive/id/draft-ietf-jmap-=
calendars-28.html">jmap-calendars</a> and <a href=3D"https://www.ietf.or=
g/archive/id/draft-ietf-calext-jscalendar-icalendar-25.html#name-class">=
jscalendar-icalendar</a>.<br></div><h2>2. Quirks with non-recurring priv=
ate events</h2><div>Non-recurring=0A private events mostly are straightf=
orward to implement consistently for=0A both JMAP and CalDAV: a calendar=
 owner can read and write events of all=0A privacy levels. A calendar sh=
aree can read and write =E2=80=9Cpublic=E2=80=9D events in=0A full, can =
read only the basic time and metadata of =E2=80=9Cprivate=E2=80=9D event=
s but cannot write them, and a sharee cannot see or otherwise access =E2=
=80=9Csecret=E2=80=9D events at all. Except=0A for the following quirk:<=
/div><div><br></div><div><b>Quirk#1</b>:=0A The existence of a =E2=80=9C=
secret=E2=80=9D event can leak to sharees with write=20=0Aaccess. A CalD=
AV client on a shared calendar may PUT, COPY or MOVE a=20=0Acalendar eve=
nt where the UID or CalDAV resource name matches that of a=20=0A=E2=80=9C=
secret=E2=80=9D event. In this case, the server has to reject the reques=
t with a <code style=3D"border-top-width:1px;border-top-style:solid;bord=
er-top-color:rgb(204, 204, 204);border-right-width:1px;border-right-styl=
e:solid;border-right-color:rgb(204, 204, 204);border-bottom-width:1px;bo=
rder-bottom-style:solid;border-bottom-color:rgb(204, 204, 204);border-le=
ft-width:1px;border-left-style:solid;border-left-color:rgb(204, 204, 204=
);border-image-outset:0;border-image-repeat:stretch;border-image-slice:1=
00%;border-image-source:none;border-image-width:1;border-top-left-radius=
:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-b=
ottom-left-radius:3px;background-color:rgb(246, 246, 246);background-pos=
ition-x:0%;background-position-y:0%;background-repeat:repeat;background-=
attachment:scroll;background-image:none;background-size:auto;background-=
origin:padding-box;background-clip:border-box;font-family:menlo, consola=
s, monospace;font-size:90%;padding-top:1px;padding-right:3px;padding-bot=
tom:1px;padding-left:3px;">CALDAV:no-uid-conflict</code> precondition, w=
hich reveals that a resource with that name or UID exists. Likewise, a J=
MAP Calendars server must reject a <code style=3D"border-top-width:1px;b=
order-top-style:solid;border-top-color:rgb(204, 204, 204);border-right-w=
idth:1px;border-right-style:solid;border-right-color:rgb(204, 204, 204);=
border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rg=
b(204, 204, 204);border-left-width:1px;border-left-style:solid;border-le=
ft-color:rgb(204, 204, 204);border-image-outset:0;border-image-repeat:st=
retch;border-image-slice:100%;border-image-source:none;border-image-widt=
h:1;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom=
-right-radius:3px;border-bottom-left-radius:3px;background-color:rgb(246=
, 246, 246);background-position-x:0%;background-position-y:0%;background=
-repeat:repeat;background-attachment:scroll;background-image:none;backgr=
ound-size:auto;background-origin:padding-box;background-clip:border-box;=
font-family:menlo, consolas, monospace;font-size:90%;padding-top:1px;pad=
ding-right:3px;padding-bottom:1px;padding-left:3px;">CalendarEvent/set</=
code> method if it would create an already existing secret <code style=3D=
"border-top-width:1px;border-top-style:solid;border-top-color:rgb(204, 2=
04, 204);border-right-width:1px;border-right-style:solid;border-right-co=
lor:rgb(204, 204, 204);border-bottom-width:1px;border-bottom-style:solid=
;border-bottom-color:rgb(204, 204, 204);border-left-width:1px;border-lef=
t-style:solid;border-left-color:rgb(204, 204, 204);border-image-outset:0=
;border-image-repeat:stretch;border-image-slice:100%;border-image-source=
:none;border-image-width:1;border-top-left-radius:3px;border-top-right-r=
adius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;b=
ackground-color:rgb(246, 246, 246);background-position-x:0%;background-p=
osition-y:0%;background-repeat:repeat;background-attachment:scroll;backg=
round-image:none;background-size:auto;background-origin:padding-box;back=
ground-clip:border-box;font-family:menlo, consolas, monospace;font-size:=
90%;padding-top:1px;padding-right:3px;padding-bottom:1px;padding-left:3p=
x;">uid</code> in that account. The server can return a <code style=3D"b=
order-top-width:1px;border-top-style:solid;border-top-color:rgb(204, 204=
, 204);border-right-width:1px;border-right-style:solid;border-right-colo=
r:rgb(204, 204, 204);border-bottom-width:1px;border-bottom-style:solid;b=
order-bottom-color:rgb(204, 204, 204);border-left-width:1px;border-left-=
style:solid;border-left-color:rgb(204, 204, 204);border-image-outset:0;b=
order-image-repeat:stretch;border-image-slice:100%;border-image-source:n=
one;border-image-width:1;border-top-left-radius:3px;border-top-right-rad=
ius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;bac=
kground-color:rgb(246, 246, 246);background-position-x:0%;background-pos=
ition-y:0%;background-repeat:repeat;background-attachment:scroll;backgro=
und-image:none;background-size:auto;background-origin:padding-box;backgr=
ound-clip:border-box;font-family:menlo, consolas, monospace;font-size:90=
%;padding-top:1px;padding-right:3px;padding-bottom:1px;padding-left:3px;=
">forbidden</code> or <code style=3D"border-top-width:1px;border-top-sty=
le:solid;border-top-color:rgb(204, 204, 204);border-right-width:1px;bord=
er-right-style:solid;border-right-color:rgb(204, 204, 204);border-bottom=
-width:1px;border-bottom-style:solid;border-bottom-color:rgb(204, 204, 2=
04);border-left-width:1px;border-left-style:solid;border-left-color:rgb(=
204, 204, 204);border-image-outset:0;border-image-repeat:stretch;border-=
image-slice:100%;border-image-source:none;border-image-width:1;border-to=
p-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius=
:3px;border-bottom-left-radius:3px;background-color:rgb(246, 246, 246);b=
ackground-position-x:0%;background-position-y:0%;background-repeat:repea=
t;background-attachment:scroll;background-image:none;background-size:aut=
o;background-origin:padding-box;background-clip:border-box;font-family:m=
enlo, consolas, monospace;font-size:90%;padding-top:1px;padding-right:3p=
x;padding-bottom:1px;padding-left:3px;">invalidProperties</code> SetErro=
r, but either leaks existence of that secret <code style=3D"border-top-w=
idth:1px;border-top-style:solid;border-top-color:rgb(204, 204, 204);bord=
er-right-width:1px;border-right-style:solid;border-right-color:rgb(204, =
204, 204);border-bottom-width:1px;border-bottom-style:solid;border-botto=
m-color:rgb(204, 204, 204);border-left-width:1px;border-left-style:solid=
;border-left-color:rgb(204, 204, 204);border-image-outset:0;border-image=
-repeat:stretch;border-image-slice:100%;border-image-source:none;border-=
image-width:1;border-top-left-radius:3px;border-top-right-radius:3px;bor=
der-bottom-right-radius:3px;border-bottom-left-radius:3px;background-col=
or:rgb(246, 246, 246);background-position-x:0%;background-position-y:0%;=
background-repeat:repeat;background-attachment:scroll;background-image:n=
one;background-size:auto;background-origin:padding-box;background-clip:b=
order-box;font-family:menlo, consolas, monospace;font-size:90%;padding-t=
op:1px;padding-right:3px;padding-bottom:1px;padding-left:3px;">uid</code=
>.</div><div><br></div><div>I=0A see this first quirk as inevitable. If =
leaking the existence of a secret event=20=0Aidentifier is a concern, th=
en the service must prevent secret events=20=0Afrom ever becoming non-se=
cret, whether by forbidding scheduling these=20=0Aevents or by forbiddin=
g an event that has ever been non-secret from=20=0Abecoming secret. In a=
ny case, even that does not prevent a client from=20=0Aenumerating rando=
m (or not so random) identifiers.<br></div><h2>3. Quirks with recurring =
private events</h2><div>For recurring private events things become more =
complicated. Currently, JSCalendar defines that patching the <code style=
=3D"border-top-width:1px;border-top-style:solid;border-top-color:rgb(204=
, 204, 204);border-right-width:1px;border-right-style:solid;border-right=
-color:rgb(204, 204, 204);border-bottom-width:1px;border-bottom-style:so=
lid;border-bottom-color:rgb(204, 204, 204);border-left-width:1px;border-=
left-style:solid;border-left-color:rgb(204, 204, 204);border-image-outse=
t:0;border-image-repeat:stretch;border-image-slice:100%;border-image-sou=
rce:none;border-image-width:1;border-top-left-radius:3px;border-top-righ=
t-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3p=
x;background-color:rgb(246, 246, 246);background-position-x:0%;backgroun=
d-position-y:0%;background-repeat:repeat;background-attachment:scroll;ba=
ckground-image:none;background-size:auto;background-origin:padding-box;b=
ackground-clip:border-box;font-family:menlo, consolas, monospace;font-si=
ze:90%;padding-top:1px;padding-right:3px;padding-bottom:1px;padding-left=
:3px;">privacy</code> property in a recurrence override must be ignored.=
 For example, a=20=0Aclient may attempt to create the following calendar=
 event with a "private" override, but in fact all=20=0Aoccurrences of th=
e event will be =E2=80=9Cpublic=E2=80=9D:</div><div><br></div><pre style=
=3D"border-top-width:1px;border-top-style:solid;border-top-color:rgb(204=
, 204, 204);border-right-width:1px;border-right-style:solid;border-right=
-color:rgb(204, 204, 204);border-bottom-width:1px;border-bottom-style:so=
lid;border-bottom-color:rgb(204, 204, 204);border-left-width:1px;border-=
left-style:solid;border-left-color:rgb(204, 204, 204);border-image-outse=
t:0;border-image-repeat:stretch;border-image-slice:100%;border-image-sou=
rce:none;border-image-width:1;border-top-left-radius:3px;border-top-righ=
t-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3p=
x;background-color:rgb(246, 246, 246);background-position-x:0%;backgroun=
d-position-y:0%;background-repeat:repeat;background-attachment:scroll;ba=
ckground-image:none;background-size:auto;background-origin:padding-box;b=
ackground-clip:border-box;font-family:menlo, consolas, monospace;font-si=
ze:90%;margin-top:7px;margin-right:0px;margin-bottom:7px;margin-left:0px=
;padding-top:7px;padding-right:10px;padding-bottom:7px;padding-left:10px=
;text-wrap-mode:wrap;white-space-collapse:preserve;overflow-wrap:break-w=
ord;">{=0A  "privacy": "public",=0A  "recurrenceOverrides": { "2026-08-2=
8T01:00:00" : { "privacy": "private" } }=0A  ...=0A}</pre><div><br></div=
><div><b>Quirk#2</b>: For <code style=3D"border-top-width:1px;border-top=
-style:solid;border-top-color:rgb(204, 204, 204);border-right-width:1px;=
border-right-style:solid;border-right-color:rgb(204, 204, 204);border-bo=
ttom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(204, 20=
4, 204);border-left-width:1px;border-left-style:solid;border-left-color:=
rgb(204, 204, 204);border-image-outset:0;border-image-repeat:stretch;bor=
der-image-slice:100%;border-image-source:none;border-image-width:1;borde=
r-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-ra=
dius:3px;border-bottom-left-radius:3px;background-color:rgb(246, 246, 24=
6);background-position-x:0%;background-position-y:0%;background-repeat:r=
epeat;background-attachment:scroll;background-image:none;background-size=
:auto;background-origin:padding-box;background-clip:border-box;font-fami=
ly:menlo, consolas, monospace;font-size:90%;padding-top:1px;padding-righ=
t:3px;padding-bottom:1px;padding-left:3px;">CalendarEvent/get</code>, an=
 unaware developer might be led to assume by the above snippet that over=
riding <code style=3D"border-top-width:1px;border-top-style:solid;border=
-top-color:rgb(204, 204, 204);border-right-width:1px;border-right-style:=
solid;border-right-color:rgb(204, 204, 204);border-bottom-width:1px;bord=
er-bottom-style:solid;border-bottom-color:rgb(204, 204, 204);border-left=
-width:1px;border-left-style:solid;border-left-color:rgb(204, 204, 204);=
border-image-outset:0;border-image-repeat:stretch;border-image-slice:100=
%;border-image-source:none;border-image-width:1;border-top-left-radius:3=
px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bot=
tom-left-radius:3px;background-color:rgb(246, 246, 246);background-posit=
ion-x:0%;background-position-y:0%;background-repeat:repeat;background-at=
tachment:scroll;background-image:none;background-size:auto;background-or=
igin:padding-box;background-clip:border-box;font-family:menlo, consolas,=
 monospace;font-size:90%;padding-top:1px;padding-right:3px;padding-botto=
m:1px;padding-left:3px;">privacy</code> is possible. They may retrieve s=
uch an event either because another=20=0Aclient created it, or the event=
 got converted from iCalendar where CLASS=0A may differ between the main=
 event and its override exceptions.</div><div><br></div><div><b>Quirk#3<=
/b>: For <code style=3D"border-top-width:1px;border-top-style:solid;bord=
er-top-color:rgb(204, 204, 204);border-right-width:1px;border-right-styl=
e:solid;border-right-color:rgb(204, 204, 204);border-bottom-width:1px;bo=
rder-bottom-style:solid;border-bottom-color:rgb(204, 204, 204);border-le=
ft-width:1px;border-left-style:solid;border-left-color:rgb(204, 204, 204=
);border-image-outset:0;border-image-repeat:stretch;border-image-slice:1=
00%;border-image-source:none;border-image-width:1;border-top-left-radius=
:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-b=
ottom-left-radius:3px;background-color:rgb(246, 246, 246);background-pos=
ition-x:0%;background-position-y:0%;background-repeat:repeat;background-=
attachment:scroll;background-image:none;background-size:auto;background-=
origin:padding-box;background-clip:border-box;font-family:menlo, consola=
s, monospace;font-size:90%;padding-top:1px;padding-right:3px;padding-bot=
tom:1px;padding-left:3px;">CalendarEvent/set</code>,=0A it is underspeci=
fied what =E2=80=9Cignoring the privacy patch=E2=80=9D means. A server=20=
=0Athat interprets it to remove privacy from the patch object can indica=
te=20=0Athat to a client in the <code style=3D"border-top-width:1px;bord=
er-top-style:solid;border-top-color:rgb(204, 204, 204);border-right-widt=
h:1px;border-right-style:solid;border-right-color:rgb(204, 204, 204);bor=
der-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(2=
04, 204, 204);border-left-width:1px;border-left-style:solid;border-left-=
color:rgb(204, 204, 204);border-image-outset:0;border-image-repeat:stret=
ch;border-image-slice:100%;border-image-source:none;border-image-width:1=
;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-ri=
ght-radius:3px;border-bottom-left-radius:3px;background-color:rgb(246, 2=
46, 246);background-position-x:0%;background-position-y:0%;background-re=
peat:repeat;background-attachment:scroll;background-image:none;backgroun=
d-size:auto;background-origin:padding-box;background-clip:border-box;fon=
t-family:menlo, consolas, monospace;font-size:90%;padding-top:1px;paddin=
g-right:3px;padding-bottom:1px;padding-left:3px;">CalendarEvent/set</cod=
e> response fields. A server that ignores but preserves the patch does n=
ot, and will return it verbatim in a later <code style=3D"border-top-wid=
th:1px;border-top-style:solid;border-top-color:rgb(204, 204, 204);border=
-right-width:1px;border-right-style:solid;border-right-color:rgb(204, 20=
4, 204);border-bottom-width:1px;border-bottom-style:solid;border-bottom-=
color:rgb(204, 204, 204);border-left-width:1px;border-left-style:solid;b=
order-left-color:rgb(204, 204, 204);border-image-outset:0;border-image-r=
epeat:stretch;border-image-slice:100%;border-image-source:none;border-im=
age-width:1;border-top-left-radius:3px;border-top-right-radius:3px;borde=
r-bottom-right-radius:3px;border-bottom-left-radius:3px;background-color=
:rgb(246, 246, 246);background-position-x:0%;background-position-y:0%;ba=
ckground-repeat:repeat;background-attachment:scroll;background-image:non=
e;background-size:auto;background-origin:padding-box;background-clip:bor=
der-box;font-family:menlo, consolas, monospace;font-size:90%;padding-top=
:1px;padding-right:3px;padding-bottom:1px;padding-left:3px;">/get</code>=
.</div><div><br></div><div><b>Quirk#4</b>: For <code style=3D"border-top=
-width:1px;border-top-style:solid;border-top-color:rgb(204, 204, 204);bo=
rder-right-width:1px;border-right-style:solid;border-right-color:rgb(204=
, 204, 204);border-bottom-width:1px;border-bottom-style:solid;border-bot=
tom-color:rgb(204, 204, 204);border-left-width:1px;border-left-style:sol=
id;border-left-color:rgb(204, 204, 204);border-image-outset:0;border-ima=
ge-repeat:stretch;border-image-slice:100%;border-image-source:none;borde=
r-image-width:1;border-top-left-radius:3px;border-top-right-radius:3px;b=
order-bottom-right-radius:3px;border-bottom-left-radius:3px;background-c=
olor:rgb(246, 246, 246);background-position-x:0%;background-position-y:0=
%;background-repeat:repeat;background-attachment:scroll;background-image=
:none;background-size:auto;background-origin:padding-box;background-clip=
:border-box;font-family:menlo, consolas, monospace;font-size:90%;padding=
-top:1px;padding-right:3px;padding-bottom:1px;padding-left:3px;">Calenda=
rEvent/set</code>, whether a <code style=3D"border-top-width:1px;border-=
top-style:solid;border-top-color:rgb(204, 204, 204);border-right-width:1=
px;border-right-style:solid;border-right-color:rgb(204, 204, 204);border=
-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(204,=
 204, 204);border-left-width:1px;border-left-style:solid;border-left-col=
or:rgb(204, 204, 204);border-image-outset:0;border-image-repeat:stretch;=
border-image-slice:100%;border-image-source:none;border-image-width:1;bo=
rder-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right=
-radius:3px;border-bottom-left-radius:3px;background-color:rgb(246, 246,=
 246);background-position-x:0%;background-position-y:0%;background-repea=
t:repeat;background-attachment:scroll;background-image:none;background-s=
ize:auto;background-origin:padding-box;background-clip:border-box;font-f=
amily:menlo, consolas, monospace;font-size:90%;padding-top:1px;padding-r=
ight:3px;padding-bottom:1px;padding-left:3px;">privacy</code> patch take=
s effect depends on what the event id refers to. For a synthetic id retr=
ieved from a recurring event when expanding recurrences in <code style=3D=
"border-top-width:1px;border-top-style:solid;border-top-color:rgb(204, 2=
04, 204);border-right-width:1px;border-right-style:solid;border-right-co=
lor:rgb(204, 204, 204);border-bottom-width:1px;border-bottom-style:solid=
;border-bottom-color:rgb(204, 204, 204);border-left-width:1px;border-lef=
t-style:solid;border-left-color:rgb(204, 204, 204);border-image-outset:0=
;border-image-repeat:stretch;border-image-slice:100%;border-image-source=
:none;border-image-width:1;border-top-left-radius:3px;border-top-right-r=
adius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;b=
ackground-color:rgb(246, 246, 246);background-position-x:0%;background-p=
osition-y:0%;background-repeat:repeat;background-attachment:scroll;backg=
round-image:none;background-size:auto;background-origin:padding-box;back=
ground-clip:border-box;font-family:menlo, consolas, monospace;font-size:=
90%;padding-top:1px;padding-right:3px;padding-bottom:1px;padding-left:3p=
x;">/query</code>, the server must process the patch as an update to a r=
ecurrence override of the base event, and <code style=3D"border-top-widt=
h:1px;border-top-style:solid;border-top-color:rgb(204, 204, 204);border-=
right-width:1px;border-right-style:solid;border-right-color:rgb(204, 204=
, 204);border-bottom-width:1px;border-bottom-style:solid;border-bottom-c=
olor:rgb(204, 204, 204);border-left-width:1px;border-left-style:solid;bo=
rder-left-color:rgb(204, 204, 204);border-image-outset:0;border-image-re=
peat:stretch;border-image-slice:100%;border-image-source:none;border-ima=
ge-width:1;border-top-left-radius:3px;border-top-right-radius:3px;border=
-bottom-right-radius:3px;border-bottom-left-radius:3px;background-color:=
rgb(246, 246, 246);background-position-x:0%;background-position-y:0%;bac=
kground-repeat:repeat;background-attachment:scroll;background-image:none=
;background-size:auto;background-origin:padding-box;background-clip:bord=
er-box;font-family:menlo, consolas, monospace;font-size:90%;padding-top:=
1px;padding-right:3px;padding-bottom:1px;padding-left:3px;">privacy</cod=
e> is ignored. If the id denotes a stand-alone recurrence instance, <cod=
e style=3D"border-top-width:1px;border-top-style:solid;border-top-color:=
rgb(204, 204, 204);border-right-width:1px;border-right-style:solid;borde=
r-right-color:rgb(204, 204, 204);border-bottom-width:1px;border-bottom-s=
tyle:solid;border-bottom-color:rgb(204, 204, 204);border-left-width:1px;=
border-left-style:solid;border-left-color:rgb(204, 204, 204);border-imag=
e-outset:0;border-image-repeat:stretch;border-image-slice:100%;border-im=
age-source:none;border-image-width:1;border-top-left-radius:3px;border-t=
op-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-ra=
dius:3px;background-color:rgb(246, 246, 246);background-position-x:0%;ba=
ckground-position-y:0%;background-repeat:repeat;background-attachment:sc=
roll;background-image:none;background-size:auto;background-origin:paddin=
g-box;background-clip:border-box;font-family:menlo, consolas, monospace;=
font-size:90%;padding-top:1px;padding-right:3px;padding-bottom:1px;paddi=
ng-left:3px;">privacy</code> is a top-level property and the patch takes=
 effect. Clients can only tell the two apart by the <code style=3D"borde=
r-top-width:1px;border-top-style:solid;border-top-color:rgb(204, 204, 20=
4);border-right-width:1px;border-right-style:solid;border-right-color:rg=
b(204, 204, 204);border-bottom-width:1px;border-bottom-style:solid;borde=
r-bottom-color:rgb(204, 204, 204);border-left-width:1px;border-left-styl=
e:solid;border-left-color:rgb(204, 204, 204);border-image-outset:0;borde=
r-image-repeat:stretch;border-image-slice:100%;border-image-source:none;=
border-image-width:1;border-top-left-radius:3px;border-top-right-radius:=
3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;backgro=
und-color:rgb(246, 246, 246);background-position-x:0%;background-positio=
n-y:0%;background-repeat:repeat;background-attachment:scroll;background-=
image:none;background-size:auto;background-origin:padding-box;background=
-clip:border-box;font-family:menlo, consolas, monospace;font-size:90%;pa=
dding-top:1px;padding-right:3px;padding-bottom:1px;padding-left:3px;">ba=
seEventId</code> property.</div><div><br></div><div><b>Quirk#5</b>:=0A R=
elated to the previous quirk, two stand-alone recurrence instances may=0A=
 have differing privacy levels and both take effect, but only as long as=
=0A no main event for these instances exists. As soon as the main event=20=
=0Aexists, their previous privacy levels become ineffective and are defi=
ned=0A by the main event.</div><div><br></div><div><b>Quirk#6</b>:=0A Fo=
r CalDAV interoperability, a server might either keep on accepting=20=0A=
but ignoring differing CLASS property values in override exceptions, or=20=
=0Ait has to rewrite the CLASS properties of these overrides during PUT.=20=
=0AFor the latter, it might also need to rewrite its existing calendar=20=
=0Aresources and instruct CalDAV clients to GET the rewritten data by bu=
mping the ETag.<br></div><h2>4. Proposal</h2><div>To address all but the=
 last quirk of recurring events, I propose the following changes:</div><=
ul><li>In JSCalendar version =E2=80=9C2.0=E2=80=9D and later, we forbid =
patching <code style=3D"border-top-width:1px;border-top-style:solid;bord=
er-top-color:rgb(204, 204, 204);border-right-width:1px;border-right-styl=
e:solid;border-right-color:rgb(204, 204, 204);border-bottom-width:1px;bo=
rder-bottom-style:solid;border-bottom-color:rgb(204, 204, 204);border-le=
ft-width:1px;border-left-style:solid;border-left-color:rgb(204, 204, 204=
);border-image-outset:0;border-image-repeat:stretch;border-image-slice:1=
00%;border-image-source:none;border-image-width:1;border-top-left-radius=
:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-b=
ottom-left-radius:3px;background-color:rgb(246, 246, 246);background-pos=
ition-x:0%;background-position-y:0%;background-repeat:repeat;background-=
attachment:scroll;background-image:none;background-size:auto;background-=
origin:padding-box;background-clip:border-box;font-family:menlo, consola=
s, monospace;font-size:90%;padding-top:1px;padding-right:3px;padding-bot=
tom:1px;padding-left:3px;">privacy</code> in a recurrence override, rath=
er than ignoring it. JMAP Calendars=20=0Aimplementations that keep on su=
pporting version =E2=80=9C1.0=E2=80=9D should accept but=20=0Astrip <cod=
e style=3D"border-top-width:1px;border-top-style:solid;border-top-color:=
rgb(204, 204, 204);border-right-width:1px;border-right-style:solid;borde=
r-right-color:rgb(204, 204, 204);border-bottom-width:1px;border-bottom-s=
tyle:solid;border-bottom-color:rgb(204, 204, 204);border-left-width:1px;=
border-left-style:solid;border-left-color:rgb(204, 204, 204);border-imag=
e-outset:0;border-image-repeat:stretch;border-image-slice:100%;border-im=
age-source:none;border-image-width:1;border-top-left-radius:3px;border-t=
op-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-ra=
dius:3px;background-color:rgb(246, 246, 246);background-position-x:0%;ba=
ckground-position-y:0%;background-repeat:repeat;background-attachment:sc=
roll;background-image:none;background-size:auto;background-origin:paddin=
g-box;background-clip:border-box;font-family:menlo, consolas, monospace;=
font-size:90%;padding-top:1px;padding-right:3px;padding-bottom:1px;paddi=
ng-left:3px;">privacy</code> patches in recurrenceOverrides and should n=
ot return such patches in <code style=3D"border-top-width:1px;border-top=
-style:solid;border-top-color:rgb(204, 204, 204);border-right-width:1px;=
border-right-style:solid;border-right-color:rgb(204, 204, 204);border-bo=
ttom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(204, 20=
4, 204);border-left-width:1px;border-left-style:solid;border-left-color:=
rgb(204, 204, 204);border-image-outset:0;border-image-repeat:stretch;bor=
der-image-slice:100%;border-image-source:none;border-image-width:1;borde=
r-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-ra=
dius:3px;border-bottom-left-radius:3px;background-color:rgb(246, 246, 24=
6);background-position-x:0%;background-position-y:0%;background-repeat:r=
epeat;background-attachment:scroll;background-image:none;background-size=
:auto;background-origin:padding-box;background-clip:border-box;font-fami=
ly:menlo, consolas, monospace;font-size:90%;padding-top:1px;padding-righ=
t:3px;padding-bottom:1px;padding-left:3px;">CalendarEvent/get</code>.</l=
i><li>For JMAP Calendars, we require the same privacy level for all cale=
ndar objects having the same <code style=3D"border-top-width:1px;border-=
top-style:solid;border-top-color:rgb(204, 204, 204);border-right-width:1=
px;border-right-style:solid;border-right-color:rgb(204, 204, 204);border=
-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(204,=
 204, 204);border-left-width:1px;border-left-style:solid;border-left-col=
or:rgb(204, 204, 204);border-image-outset:0;border-image-repeat:stretch;=
border-image-slice:100%;border-image-source:none;border-image-width:1;bo=
rder-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right=
-radius:3px;border-bottom-left-radius:3px;background-color:rgb(246, 246,=
 246);background-position-x:0%;background-position-y:0%;background-repea=
t:repeat;background-attachment:scroll;background-image:none;background-s=
ize:auto;background-origin:padding-box;background-clip:border-box;font-f=
amily:menlo, consolas, monospace;font-size:90%;padding-top:1px;padding-r=
ight:3px;padding-bottom:1px;padding-left:3px;">uid</code>. A <code style=
=3D"border-top-width:1px;border-top-style:solid;border-top-color:rgb(204=
, 204, 204);border-right-width:1px;border-right-style:solid;border-right=
-color:rgb(204, 204, 204);border-bottom-width:1px;border-bottom-style:so=
lid;border-bottom-color:rgb(204, 204, 204);border-left-width:1px;border-=
left-style:solid;border-left-color:rgb(204, 204, 204);border-image-outse=
t:0;border-image-repeat:stretch;border-image-slice:100%;border-image-sou=
rce:none;border-image-width:1;border-top-left-radius:3px;border-top-righ=
t-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3p=
x;background-color:rgb(246, 246, 246);background-position-x:0%;backgroun=
d-position-y:0%;background-repeat:repeat;background-attachment:scroll;ba=
ckground-image:none;background-size:auto;background-origin:padding-box;b=
ackground-clip:border-box;font-family:menlo, consolas, monospace;font-si=
ze:90%;padding-top:1px;padding-right:3px;padding-bottom:1px;padding-left=
:3px;">CalendarEvent/set</code> that, after all changes in the <code sty=
le=3D"border-top-width:1px;border-top-style:solid;border-top-color:rgb(2=
04, 204, 204);border-right-width:1px;border-right-style:solid;border-rig=
ht-color:rgb(204, 204, 204);border-bottom-width:1px;border-bottom-style:=
solid;border-bottom-color:rgb(204, 204, 204);border-left-width:1px;borde=
r-left-style:solid;border-left-color:rgb(204, 204, 204);border-image-out=
set:0;border-image-repeat:stretch;border-image-slice:100%;border-image-s=
ource:none;border-image-width:1;border-top-left-radius:3px;border-top-ri=
ght-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:=
3px;background-color:rgb(246, 246, 246);background-position-x:0%;backgro=
und-position-y:0%;background-repeat:repeat;background-attachment:scroll;=
background-image:none;background-size:auto;background-origin:padding-box=
;background-clip:border-box;font-family:menlo, consolas, monospace;font-=
size:90%;padding-top:1px;padding-right:3px;padding-bottom:1px;padding-le=
ft:3px;">/set</code> have been applied, causes the privacy of a given <c=
ode style=3D"border-top-width:1px;border-top-style:solid;border-top-colo=
r:rgb(204, 204, 204);border-right-width:1px;border-right-style:solid;bor=
der-right-color:rgb(204, 204, 204);border-bottom-width:1px;border-bottom=
-style:solid;border-bottom-color:rgb(204, 204, 204);border-left-width:1p=
x;border-left-style:solid;border-left-color:rgb(204, 204, 204);border-im=
age-outset:0;border-image-repeat:stretch;border-image-slice:100%;border-=
image-source:none;border-image-width:1;border-top-left-radius:3px;border=
-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-=
radius:3px;background-color:rgb(246, 246, 246);background-position-x:0%;=
background-position-y:0%;background-repeat:repeat;background-attachment:=
scroll;background-image:none;background-size:auto;background-origin:padd=
ing-box;background-clip:border-box;font-family:menlo, consolas, monospac=
e;font-size:90%;padding-top:1px;padding-right:3px;padding-bottom:1px;pad=
ding-left:3px;">uid</code> to become ambiguous within an account must be=
 rejected. For scheduling=20=0Ainvites, an updated event keeps whatever =
privacy was assigned to an=20=0Aexisting event with that <code style=3D"=
border-top-width:1px;border-top-style:solid;border-top-color:rgb(204, 20=
4, 204);border-right-width:1px;border-right-style:solid;border-right-col=
or:rgb(204, 204, 204);border-bottom-width:1px;border-bottom-style:solid;=
border-bottom-color:rgb(204, 204, 204);border-left-width:1px;border-left=
-style:solid;border-left-color:rgb(204, 204, 204);border-image-outset:0;=
border-image-repeat:stretch;border-image-slice:100%;border-image-source:=
none;border-image-width:1;border-top-left-radius:3px;border-top-right-ra=
dius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;ba=
ckground-color:rgb(246, 246, 246);background-position-x:0%;background-po=
sition-y:0%;background-repeat:repeat;background-attachment:scroll;backgr=
ound-image:none;background-size:auto;background-origin:padding-box;backg=
round-clip:border-box;font-family:menlo, consolas, monospace;font-size:9=
0%;padding-top:1px;padding-right:3px;padding-bottom:1px;padding-left:3px=
;">uid</code>.</li><li>When converting from=20=0AiCalendar to JSCalendar=
, the most restrictive CLASS value of a main=20=0Acomponent and all its =
recurrence overrides converts to the privacy of=20=0Athe converted Event=
 or Task. For stand-alone recurrence instances with the same UID, the mo=
st restrictive of their CLASS values converts to=20=0Athe privacy of eac=
h converted object.</li></ul><div>This leaves quirk #6, for I see no way=
 around it, regardless if we keep the existing <code style=3D"border-top=
-width:1px;border-top-style:solid;border-top-color:rgb(204, 204, 204);bo=
rder-right-width:1px;border-right-style:solid;border-right-color:rgb(204=
, 204, 204);border-bottom-width:1px;border-bottom-style:solid;border-bot=
tom-color:rgb(204, 204, 204);border-left-width:1px;border-left-style:sol=
id;border-left-color:rgb(204, 204, 204);border-image-outset:0;border-ima=
ge-repeat:stretch;border-image-slice:100%;border-image-source:none;borde=
r-image-width:1;border-top-left-radius:3px;border-top-right-radius:3px;b=
order-bottom-right-radius:3px;border-bottom-left-radius:3px;background-c=
olor:rgb(246, 246, 246);background-position-x:0%;background-position-y:0=
%;background-repeat:repeat;background-attachment:scroll;background-image=
:none;background-size:auto;background-origin:padding-box;background-clip=
:border-box;font-family:menlo, consolas, monospace;font-size:90%;padding=
-top:1px;padding-right:3px;padding-bottom:1px;padding-left:3px;">privacy=
</code> definitions or update them as I propose. The core issue is that =
CalDAV=20=0Adoes not define anything about the CLASS property and its iC=
alendar=20=0Adefinition is very vague, too. In our implementation, we wi=
ll aim to=20=0Arewrite newly created and existing iCalendar data to matc=
h JMAP=20=0ACalendars and JSCalendar semantics as much as possible.</div=
><div><br></div><div>Regards,<br> Robert</div></body></html>
--566c0d4646d1c9c6388758e55c3595de1210794e--

