forked from openssi/peer-did-method-spec
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdid-docs.html
501 lines (462 loc) · 29.2 KB
/
did-docs.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
<h2>DID docs</h2>
<section>
<h3>Backing Storage</h3>
<p>At first glance, it might seem that parties dealing in peer DID docs could just store raw DID docs.
However, this is not the case. Any time a DID doc evolves, proof that the evolution is authorized must be found
in the DID doc's previous state. If an agent is offline for an extended period (e.g., a phone is lost in the
couch cushions for a week), multiple evolutions may have occurred by the time it reconnects--and it cannot
accept the latest state of the doc without validating the sequence of changes it underwent to get there. Agents
must be able to prove to one another that the state they hold is correct, and they must be able to answer
questions about <em>what</em> the state used to be, and <em>who</em> authorized <em>which</em> changes, <em>when
</em>.
</p>
<p>This means that peer DID docs need to be associated with some type of <dfn>backing storage</dfn> that adds
metadata and history to the simple content of the docs themselves.
</p>
<p>The backing storage for peer DID docs is therefore a sequence of <dfn>deltas</dfn>. Each delta is a JSON object
in this form:</p>
<pre>
{
"id": <a UUID>,
"change": <base64 encoding of a change fragment>,
"by": [ {"key": <id of key>, "sig": <signature value>} ... ],
"when": <ISO8601 UTC timestamp with at least second precision>
}
</pre>
<p>Here, a <dfn>change fragment</dfn> is a sparse version of a DID doc that shows just the section being
updated. The <code>by</code> property contains one or more key+signature pairs over the raw (non-base64-encoded)
JSON text of the change fragment, and <code>when</code> contains a value supplied by the system clock. This
value is like timestamps on an email header—not guaranteed to be highly accurate, but useful for rough
analysis.
</p>
<p>Deltas are like database transactions, in that they may embody multiple changes that must be performed together.
For example, key rotation involves both a key addition and a key deletion, performed as a unit.
All changes in a single delta MUST share a common authorization; it is invalid to combine into one delta changes
requiring two different authorizations, as this makes authorization of each individual change ambiguous.
</p>
<p>A resolved peer DID doc at any given point in time is a view or projection of the cumulative effect of all
known deltas up to that point, with resolution adding the DID value itself to the composite <a>stored
variant</a> of DID doc data.
</p>
<p class="note">Early explorations of this DID method used the term <dfn>microledger</dfn> for backing storage. This
term has some resonance, in that backing storage is an append-only record of immutable transactions. However,
we have chosen not to emphasize the term here, because it may feel intimidating to some, and because the actual
storage mechanism in an implementation of this method could be a simple file, a database, or anything else that
provides equivalent features.
</p>
<p>In the discussion about generating a <a>numeric basis</a> for a peer DID, the <a>genesis version</a> of a peer
DID doc was defined. The genesis version is the one associated with the first delta committed to backing
storage. In the first delta, <code>change</code> contains the genesis state of the doc, <code>by</code> holds
a signature from any key defined internally in the genesis state, and <code>when</code> is populated by the
system clock.
</p>
<p class="note">The genesis state can be authorized by <em>any</em> internally defined key, even if that key is has a very
limited <a>trust profile</a>. This is because the authorization for genesis state is only proving that the
creator of the DID doc has not been shadowed by a malicious man-in-the-middle; no other test of authorization
is performed.
</p>
</section>
<section>
<h3>CRDTs</h3>
<p>Because peer DID docs can be updated by multiple parties at the same time (as when two agents of the same identity
owner rotate their keys without coordinating), they have the potential for merge conflicts. Resolving these
merge conflicts could introduce much complexity in our DID method. However, peer DID docs use a clever
technique that avoids much of the pain. The backing storage for peer DID docs is carefully defined to
be a <a target="wikipedia" href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type">conflict-free
replicated data type</a> (<dfn>CRDT</dfn>).</p>
<p>CRDTs are a deep subject unto themselves, with a body of academic research; additional study is recommended.
They are behind familiar experiences like simultaneous, distributed editing of Google Docs, which never raises
error messages about merge conflicts. Here, we will not explain their theory; we will simply note some key
requirements that they impose on DID docs and their <a>backing storage</a>.</p>
<ol id="crdt-rules">
<li>Core building blocks—keys, rules, and service endpoints— must be assigned
<code>id</code> properties in a way that prevents collisions.</li>
<li>All items with <code>id</code> properties must be immutable. They can be deleted, but they can never have
a different state than they were created with. The effect of modifying an item can be achieved by
deleting an old item and replacing it with a new item that has a different <code>id</code> and updated
properties.</li>
<li>Deleting an item is accomplished by adding its <code>id</code> to a <dfn>deletion list</dfn>. This list
is a root property of the <a>stored variant</a> of a DID doc and is named <code>deleted</code>. It is
suppressed when generating a <a>resolved variant</a> of the DID doc, so it is only visible in the deltas
of <a>backing storage</a>. See <a href="#deleting-a-publickey">this clarifying example</a>.</li>
<li>When any party encounters two versions of a peer DID doc, one of which has an item in undeleted state and
one of which has the item in a deleted state, the item's state should be considered deleted, regardless
of relative timing of the two versions. This eliminates the possibility that a malicious party could
attempt to take advantage of differences in system clocks or sequencing.</li>
</ol>
<p>These rules do not guarantee that conflicting state can never exist. However, they eliminate most ordering
concerns, and they provide a framework that allows conflicting state to be resolved automatically. No human
judgment is ever required, and all non-malicious parties to a relationship will agree on the proper course of
action in every circumstance. Details about CRDT requirements are documented below, in the sections about
individual parts of the DID doc. These will make the CRDT semantics and the technique clearer.</p>
</section>
<section>
<h3><code><dfn>publicKey</dfn></code></h3>
<p>The <code>publicKey</code> section of a peer DID document, in either stored or resolved form, is as you would
expect from a reading of the <a target="didspec" href="https://w3c-ccg.github.io/did-spec/#authentication">DID
spec</a>. Peer DID docs MUST define all of their internal and external keys in this section; although the DID
spec permits inline definitions in the <code>authentication</code> and <code>authorization</code> sections of a
DID doc, this DID method removes that option to simplify the possible permutations of a <a>change fragment</a>.
</p>
<p>All <code>id</code> properties associated with a key defined internally in the DID doc MUST be in
relative, not absolute form (that is, "#xyz" instead of "did:peer:abc#xyz"). IDs for keys defined
interally may be generated in either of these two ways:
</p>
<ul>
<li>The first 8 characters of the key's unique representation may be used as the <code>id</code>, if this
provides enough uniqueness within the document. (Global uniqueness is irrelevant.) For example, if the
key is defined with a <code>publicKeyBase58</code> property value that begins with <code>H3C2AVvL</code>,
then its <code>id</code> would be <code>H3C2AVvL</code>; a key with a <code>publicKeyHex</code> property
that begins with <code>02b97c30</code> would have an <code>id</code> of <code>02b97c30</code>, and a key
with a <code>publicKeyPem</code> property that begins, after its <code>-----BEGIN PUBLIC KEY</code>
delimiter, with the value <code>izfrNTmQ</code>, would have an <code>id</code> of <code>izfrNTmQ</code>.
</li>
<li>A standard 128-bit UUID of type 1, 2, 3, 4, or 5 (see [[RFC4122]]), rendered with hyphens and in lower
case, may be used as the<code>id</code> instead.</li>
</ul>
<p class="note">All IDs in DID docs, including these, should be considered case-sensitive. Ignoring case will result
in invalid comparisons for key <code>id</code>s based on encodings that are not hex.
</p>
<p>Because peer DIDs usually operate independent of global registries, external references (where <code>controller</code>
is not <code>#id</code>) will probably be rare, with the possible exception of <a target="aries"
href="https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0046-mediators-and-relays/README.md">
mediator</a> keys that have the <a>route</a> privilege.
</p>
<pre class="example" title="A publicKey section">
{
...
"publicKey": [
{
"id": "H3C2AVvL",
"type": "Ed25519VerificationKey2018",
"controller": "#id",
"publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"
}, {
"id": "02b97c30",
"type": "Secp256k1VerificationKey2018",
"controller": "#id",
"publicKeyHex": "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71"
}, {
"id": "izfrNTmQ",
"type": "RsaVerificationKey2018",
"controller": "#id",
"publicKeyPem": "-----BEGIN PUBLIC KEY\r\nizfrNTmQ...END PUBLIC KEY-----\r\n"
}
]
...
}
</pre>
<p class="note">This DID method is relatively agnostic with respect to key types. However, elliptic curve
keys (Ed25519, Secp256, etc) are recommended because the keys are smaller, the number
of bits of security are greater, and the computations more efficient, than RSA key types.
</p>
<section>
<h4 id="adding-a-publickey">Adding a key</h4>
<p>To add a new key with <code>id</code> = <code>Mv6gmMNa</code> to a peer DID doc, you might create
a <a>change fragment</a> of the DID doc that looks like this:
</p>
<pre class="example" title="Change fragment that adds a new key">
{
"publicKey": [
{
"id": "Mv6gmMNa",
"type": "Ed25519VerificationKey2018",
"controller": "#id",
"publicKeyBase58": "Mv6gmMNam3uVAjH3C2AVvLZZn6z3wXmqPVpfkcJCwDwn"
}
],
"authorization": { "profiles": [ {"key": "#Mv6gmMNa", "roles", ["edge"]} ] }
}
</pre>
<p>Change fragments only show where something is <em>appended</em> to either normal lists or the <a>deletion
list</a>. Here, the single entries under <code>publicKey</code> and <code>authorization.profiles</code> are
additions, not replacements, to the lists that already contain values. (The <a>trust profile</a> embodied in
the<code>authorization.profiles</code> associates the new key with privileges. This must be done at creation
time. It is discussed under <a href="#authorization"><code>authorization</code></a>.) The <a>delta</a> that
encapsulates this update would take the above JSON text, base64-encode it, and assign the result to the
delta's <code>change</code> property. It would need to be authorized by a key with the <a>key_admin</a>
privilege, evidenced in the delta's <code>by</code> field. The resulting delta might look like this:
</p>
<pre class="example" title="Delta that adds a new key">
{
"id": "040aaa5e-1a27-40d8-8d53-13a00b82d235",
"change": "ewogICJwdWJsaWNLZXkiOiBbCiAgICB...ozd1htcVBWcGZrY0pDd0R3biIKICAgIH0KICBdCn0=",
"by": [ {"key": "H3C2AVvL", "sig": ""if8ooA+32YZc4SQBvIDDY9tgTa...i4VvND87PUqq5/0vsNFEGIIEDA=="} ],
"when": "2019-07-18T15:49:22.03Z"
}
</pre>
</section>
<section>
<h4 id="deleting-a-publickey">Deleting a key</h4>
<p>Any key can be deleted by appending its <code>id</code> to the <code>deleted</code> list at the root of
the stored version of a peer DID doc, and by removing its corresponding entry from the <code>publicKey</code>
section. Only the append-to-<code>deleted</code> operation needs to be represented in the <a>change
fragment</a>; the corresponding removal from the <code>publicKey</code> section is implied. For example, to
delete a key with <code>id</code> = <code>izfrNTmQ</code>, your <a>change fragment</a> would look like
this:
</p>
<pre class="example" title="Change fragment that deletes a key">
{
"deleted": [ "izfrNTmQ" ]
}
</pre>
<p>This fragment would be base64-encoded and signed in a corresponding delta, in much the same way that
the fragment was handled in the example about adding a key.</p>
</section>
</section>
<section>
<h3><code><dfn>authentication</dfn></code></h3>
<p>The <code>authentication</code> section of a peer DID document conforms to <a target="didspec"
href="https://w3c-ccg.github.io/did-spec/#authentication">standard practice described in the DID spec</a>,
with the constraint that only references, not inline key definitions, are allowed. Any keys referenced
here may be used to authenticate or login as the DID.
</p>
<p>However, the meaning of "authenticate" or "login" deserves comment, because there is a tendency in DID
circles to reference too many keys in this section, and because heavy use of this section is discouraged
with peer DIDs.
</p>
<p>A <dfn>sovereign domain</dfn> (the scope of things under the control of a single, sovereign identity owner) may
include keys that represent the identity owner with greater or lesser degrees of trust. For example, the
identity owner may hold one key on paper, locked in a safe; she may hold another key on a mobile device's
secure enclave, protected by a biometric; she may hold a third key on a server in a config file protected
only by an ACL. These keys may all be legitimate evidence that the DID subject is interacting, but
they are not equally strong evidence. Only the strongest forms of evidence should be accepted as a
basis for authentication when stakes are high.</p>
<p>This is not unlike what happens on the political landscape. A low-level diplomat and the president / premier
/ prime minister of a nation are both authentic representatives of their people—but the scope of their
privileges, and the concomitant trust, are different. Only the highest-level officials should be signing
binding international treaties.
</p>
<p>The <a href="#authorization"><code>authorization</code> section</a> of peer DID docs, described next, is
where fine-grained privileges are described. The <code>authentication</code> section is a blunt instrument,
incapable of distinguishing between these levels of trust, and incapable of associating trust with anything
more complex than a single key. It is supported for historical reasons, and because most so-called "DID
Authentication" use cases assume it will be populated. With peer DIDs, the strong recommendation is that
only keys held by the most trusted proxies should be referenced here—only list the equivalent of your
president / premier / prime minister keys in this section, not the equivalent of keys for your low-level
diplomats. This will prevent impersonation by less trusted keys when stakes are high.
</p>
<section>
<h4 id="adding-key-to-authn">Adding a key</h4>
<p>As mentioned earlier, all objects having an <code>id</code> property in peer DID docs are immutable. This
means that a key cannot change its status with respect to the <code>authentication</code> section of a peer
DID doc during its lifetime in that doc; it must be declared as an authentication key when it is first
defined. A <a>change fragment</a> for adding an internally defined key and giving it status in the
<code>authentication</code> section at the same time might look like this:
</p>
<pre class="example" title="Change fragment that adds a new key with authentication privileges">
{
"publicKey": [
{
"id": "Mv6gmMNa",
"type": "Ed25519VerificationKey2018",
"controller": "#id",
"publicKeyBase58": "Mv6gmMNam3uVAjH3C2AVvLZZn6z3wXmqPVpfkcJCwDwn"
}
],
"authentication": [ "#Mv6gmMNa" ],
"authorization": { "profiles": [ {"key": "#Mv6gmMNa", "roles", ["edge"]} ] }
}
</pre>
<p class="note">Adding a key with authentication privileges can only be authorized by a key that has the
<a>key_admin</a> privilege.</p>
<h4 id="deleting-key-from-authn">Deleting a key</h4>
<p>Because a key's authentication privileges are immutable, the only way to take away authentication
privileges from a key is to remove it entirely from the DID doc. See
<a href="#example-4-change-fragment-that-deletes-a-key">Example 4</a>.</p>
</section>
</section>
<section>
<h3><code><dfn>authorization</dfn></code></h3>
<p class="note">This section of a peer DID docs governs how DID docs are updated and DIDComm trust flows. It doesn't
contemplate custom authorization schemes such as peers might develop to give each other spending limits or
access to sensitive data. Those matters are better formalized with something like a verifiable credential, or a
simple digitally signed permission slip.
</p>
<p>Peer DID docs organize their <code>authorization</code> section into two lists. The first, <code>profiles
</code>, gives a <dfn>trust profile</dfn> for each key, as expressed by named roles the key holds. These
named roles are arbitrary strings chosen by the implementer; since they are only used to match against
rules in the second list, their meaning in normal language does not need to be understood by a party
wishing to support correct semantics.
</p>
<p class="note">Changes to the <code>profiles</code> list MUST be performed when a key is added or deleted;
the <a>trust profile</a> of a key cannot change after it is created.</p>
<p>The second list contains <a href="https://evernym.github.io/sgl" target="sgl">SGL</a> rules that grant
privileges according to named roles. Here is an example:
</p>
<pre class="example" title="An authorization section">
“authorization”: {
"profiles": [
{"key": "#Mv6gmMNa", "roles", ["edge"]}, // an "edge" key
{"key": "#izfrNTmQ", "roles", ["edge", "biometric"]}, // an "edge" and a "biometric" key
{"key": "#02b97c30", "roles", ["cloud"]}, // a "cloud" key
{"key": "#H3C2AVvL", "roles", ["offline"]}, // an "offline" key
],
"rules": [
{
"grant": ["route", "authcrypt"],
"when": {"roles": "cloud"},
"id": "98c2c9cc"
},
{
"grant": ["authcrypt", "plaintext", "sign"],
"when": {"roles": "edge"},
"id": "e1e7d7bc"
},
{
"grant": ["key_admin", "se_admin", "rules_admin"],
"when": {
"any": [{"roles": "offline"}, {"roles": "biometric"}],
"n": 2
}
"id": "8586d26c"
}
]
}
</pre>
<p>In plain english, these rules say:</p>
<ul>
<li>Let a key have the <a>route</a> and <a>authcrypt</a> privileges when its set of roles contains "cloud".</li>
<li>Let a key have the <a>authcrypt</a>, <a>plaintext</a> and <a>sign</a> privileges when its set of roles
contains "edge".</li>
<li>Let any 2 keys, acting together, have the <a>key_admin</a>, <a>se_admin</a>, and <a>rules_admin</a>
privileges when their roles each contain either "offline" or "biometric".</li>
</ul>
<p>Far more elaborate rules can be constructed. See the <a target="sgl" href="https://evernym.github.io/sgl">
SGL documentation</a> for details.</p>
<section>
<h4>Privilege Inventory</h4>
<p>Unlike role names, the privileges named in these rules DO need to be understood by code that parses peer
DID docs; it is these privileges that others in a relationship should enforce. Possible values include:</p>
<dl>
<dt><dfn>route</dfn></dt>
<dd>The holder of this privilege is allowed to receive and decrypt <a target="aries"
href="https://github.com/hyperledger/aries-rfcs/tree/master/concepts/0094-cross-domain-messaging#required-mediators-process-forward-messages">
DIDComm <code>forward</code> messages</a> encrypted for itself, and to forward the contained
<a href="https://github.com/hyperledger/aries-rfcs/blob/master/features/0019-encryption-envelope/README.md"
target="aries">DIDComm encryption envelope</a> to another key. Holders of these keys thus become aware
of the timing and size of some incoming messages for the recipient (though not of the messages' senders
or content). This privilege is required and appropriate for any cloud agent or <a target="aries"
href="https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0046-mediators-and-relays/README.md">
mediator</a> that exposes a service endpoint on behalf of mobile devices or other agents behind a
firewall.</dd>
<dt><dfn>authcrypt</dfn></dt>
<dd>The holder of this privilege is allowed to create messages and send them with <a target="wikipedia"
href="https://en.wikipedia.org/wiki/Authenticated_encryption">authenticated encryption</a>
that reveals the identity of the sender to the reciever. Most agents are likely to have this privilege,
but one designed for passive reception only (e.g., on an IoT sensor) might have it removed; doing so
would prevent a hacker from co-opting such a key into sending in a trusted way. Messages that are
authcrypted by a key that lacks this privilege should be rejected as illegal.</dd>
<dt><dfn>plaintext</dfn></dt>
<dd>The holder of this privilege can see <a target="aries"
href="https://github.com/hyperledger/aries-rfcs/tree/master/features/0044-didcomm-file-and-mime-types#didcomm-messages-dm">
plaintext DIDComm messages</a> intended for an identity owner engaged in a <a target="aries"
href="https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0003-protocols/README.md">protocol
</a>. External parties sending to the owner of a given DIDDoc should multiplex encrypt for all keys that
hold this privilege, except in special circumstances.</dd>
<dt><dfn>sign</dfn></dt>
<dd><p>The holder of this privilege can incur binding, contractual obligations on behalf of the DID subject.
This may actually be a better test for login or authentication, in many cases, than whether a key appears
in the <a href="#authentication"><code>authentication</code> section</a> of the DID doc; it depends on
what trust is imputed after login.</p>
<p class="note">Any key holder can sign a DIDComm message; doing so gives non-repudiation to the
message. However, remember our <a href="#authentication">earlier discussion</a> about low-level
diplomats versus the president or prime minister of a country—both are authentic officials,
but only the president or prime minister's signature is binding on an international treaty. This
privilege makes explicit that sort of special binding-the-owner status; any other signatures
serve to prove origin with a specific agent but not the endorsement of the DID subject.
</p></dd>
<dt><dfn>key_admin</dfn></dt>
<dd>The holder of this privilege can add or remove other keys from a peer DID doc's <a href="#publickey">
<code>publicKey</code> section</a>, <a href="#authentication"><code>authentication</code> section</a>,
or <code>authorization.profiles</code> list. Typically this privilege is held only by very privileged
keys, or by combinations of keys, to prevent hackers who co-opt one device from adding new, malicious
keys to the inventory.
</dd>
<dt><dfn>se_admin</dfn></dt>
<dd><p>The holder of this privilege can add or remove items from a peer DID doc's <a href="#service">
<code>service</code> section</a>.</p>
</dd>
<dt><dfn>rules_admin</dfn></dt>
<dd><p>The holder of this privilege can add or remove rules from a peer DID doc's <code>authorization.rules
</code> list. Typically this privilege is held only by very privileged keys, or by combinations of keys.</p>
</dd>
<dt><dfn>rotate</dfn></dt>
<dd>The holder of this privilege can replace its associated key definition, and all references to that
key throughout a DID doc, with a new key definition and references, in a single delete-and-add
operation. If key #2 has this privilege and exercises it, the result is that key #2 is revoked from
the doc, but a new key (perhaps key #7) appears with exactly the same profile. This privilege is
assumed to be held by all keys unless rules specify otherwise.
</dd>
</dl>
</section>
<section>
<h4>Adding a rule</h4>
<p>Notice that rules have <code>id</code> properties. The IDs for these rules can be assigned by any convenient
method that provides collision resistance across concurrent updates.
</p>
<p>These <code>id</code>s give rules the same <a>CRDT</a> semantics as keys
and service endpoints—they can be added or removed with deltas, but never modified. A <a>change
fragment</a> that adds a new rule might be:
</p>
<pre class="example" title="Change fragment that adds a new rule">
{
"authorization": {
"rules": [
{
"grant": ["route", "authcrypt"],
"when": {"roles": "cloud"},
"id": "rule-7"
}
]
}
}
</pre>
<p class="note">Both adding and deleting a rule must be authorized by a key with the <a>key_admin</a> privilege.
Care should be taken to bundle additions and deletions of rules in such a way that once a full <a>delta</a>
is applied, the new state provides adequate privileges for all existing keys.
</p>
</section>
<section>
<h4>Deleting a rule</h4>
<p>A <a>change fragment</a> that deletes a rule might look like this:
</p>
<pre class="example" title="Change fragment that deletes a rule">
{
"deleted": [ "rule-7" ]
}
</pre>
<p>See the note above, in the <a href="#adding-a-rule">Adding a rule</a> section, about privileges and
transactional logic when deleting rules.</p>
</section>
</section>
<section>
<h3><code><dfn>service</dfn></code></h3>
<p>This section matches the <a target="didspec" href="https://w3c-ccg.github.io/did-spec/#service-endpoints">
general description of service endpoints in the DID Spec</a>. Any changes to this section of the DID doc
must be authorized by a key with the <a>se_admin</a> privilege.
</p>
<section>
<h4>Adding a service endpoint</h4>
<p>A <a>change fragment</a> that adds a service endpoint might look like this:</p>
<pre class="example" title="Change fragment that adds a service endpoint">
{
"service": [
{
"id": "#agent",
"type": "AgentService",
"serviceEndpoint": "https://agents-r-us.com"
}
]
}
</pre>
<section>
<h4>Deleting a service endpoint</h4>
<p>A <a>change fragment</a> that deletes a service endpoint might look like this:</p>
<pre class="example" title="Change fragment that deletes a service endpoint">
{
"deleted": [ "#agent" ]
}
</pre>
</section>
</section>