idnits 2.17.1 draft-dunglas-mercure-02.txt: Checking boilerplate required by RFC 5378 and the IETF Trust (see https://trustee.ietf.org/license-info): ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/1id-guidelines.txt: ---------------------------------------------------------------------------- == The page length should not exceed 58 lines per page, but there was 1 longer page, the longest (page 1) being 564 lines Checking nits according to https://www.ietf.org/id-info/checklist : ---------------------------------------------------------------------------- ** The document seems to lack an Introduction section. ** The document seems to lack an IANA Considerations section. (See Section 2.2 of https://www.ietf.org/id-info/checklist for how to handle the case when there are no actions for IANA.) ** There is 1 instance of too long lines in the document, the longest one being 8 characters in excess of 72. Miscellaneous warnings: ---------------------------------------------------------------------------- == The copyright year in the IETF Trust and authors Copyright Line does not match the current year == The document seems to lack the recommended RFC 2119 boilerplate, even if it appears to use RFC 2119 keywords. (The document does seem to have the reference to RFC 2119 which the ID-Checklist requires). == The expression 'MAY NOT', while looking like RFC 2119 requirements text, is not defined in RFC 2119, and should not be used. Consider using 'MUST NOT' instead (if that is what you mean). Found 'MAY NOT' in this paragraph: An application CAN send events directly to the subscribers, without using an external hub server, if it is able to do so. In this case, it *MAY NOT* implement the endpoint to publish updates. == Using lowercase 'not' together with uppercase 'MUST', 'SHALL', 'SHOULD', or 'RECOMMENDED' is not an accepted usage according to RFC 2119. Please use uppercase 'NOT' together with RFC 2119 keywords (if that is what you mean). Found 'SHOULD not' in this paragraph: Also, when the client is a web browser, to be resilient to Cross-site Scription (XSS) attacks (https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)), the JWT SHOULD not be made accessible to JavaScript scripts. It's main reason why, when the client is a web browser, using "HttpOnly" cookies as authorization mechanism SHOULD always be prefered. -- The document date (22 October 2018) is 2012 days in the past. Is this intentional? -- Found something which looks like a code comment -- if you have code sections in the document, please surround them with '' and '' lines. Checking references for intended status: Informational ---------------------------------------------------------------------------- ** Obsolete normative reference: RFC 2818 (Obsoleted by RFC 9110) ** Obsolete normative reference: RFC 5988 (Obsoleted by RFC 8288) ** Obsolete normative reference: RFC 7230 (Obsoleted by RFC 9110, RFC 9112) -- Obsolete informational reference (is this intentional?): RFC 7386 (Obsoleted by RFC 7396) Summary: 6 errors (**), 0 flaws (~~), 5 warnings (==), 3 comments (--). Run idnits with the --verbose option for more detailed information about the items above. -------------------------------------------------------------------------------- 2 Network Working Group K. Dunglas 3 Internet-Draft Les-Tilleuls.coop 4 Intended status: Informational 22 October 2018 5 Expires: 25 April 2019 7 The Mercure Protocol 8 draft-dunglas-mercure-02 10 Abstract 12 Mercure is a protocol allowing to push data updates to web browsers 13 and other HTTP clients in a fast, reliable and battery-efficient way. 14 It is especially useful to publish real-time updates of resources 15 served through web APIs, to reactive web and mobile apps. 17 Status of This Memo 19 This Internet-Draft is submitted in full conformance with the 20 provisions of BCP 78 and BCP 79. 22 Internet-Drafts are working documents of the Internet Engineering 23 Task Force (IETF). Note that other groups may also distribute 24 working documents as Internet-Drafts. The list of current Internet- 25 Drafts is at https://datatracker.ietf.org/drafts/current/. 27 Internet-Drafts are draft documents valid for a maximum of six months 28 and may be updated, replaced, or obsoleted by other documents at any 29 time. It is inappropriate to use Internet-Drafts as reference 30 material or to cite them other than as "work in progress." 32 This Internet-Draft will expire on 25 April 2019. 34 Copyright Notice 36 Copyright (c) 2018 IETF Trust and the persons identified as the 37 document authors. All rights reserved. 39 This document is subject to BCP 78 and the IETF Trust's Legal 40 Provisions Relating to IETF Documents (http://trustee.ietf.org/ 41 license-info) in effect on the date of publication of this document. 42 Please review these documents carefully, as they describe your rights 43 and restrictions with respect to this document. Code Components 44 extracted from this document must include Simplified BSD License text 45 as described in Section 4.e of the Trust Legal Provisions and are 46 provided without warranty as described in the Simplified BSD License. 48 Table of Contents 50 1. Terminology 51 2. Discovery 52 3. Subscriptions 53 4. Publication 54 5. Authorization 55 5.1. Publishers 56 5.2. Subscribers 57 6. Re-Connection and State Reconciliation 58 7. Encryption 59 8. Security Considerations 60 9. References 61 9.1. Normative References 62 9.2. Informative References 63 Author's Address 65 1. Terminology 67 The keywords MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, 68 SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL, when they appear in this 69 document, are to be interpreted as described in [RFC2119]. 71 * Topic: An HTTP [RFC7230] or HTTPS [RFC2818] topic URL. The unit 72 to which one can subscribe to changes. 74 * Publisher: An owner of a topic. Notifies the hub when the topic 75 feed has been updated. As in almost all pubsub systems, the 76 publisher is unaware of the subscribers, if any. Other pubsub 77 systems might call the publisher the "source". Typically a 78 website or a web API, but can also be a web browser. 80 * Subscriber: A client application that subscribes to real-time 81 updates of topics. Typically a Progressive Web App or a Mobile 82 App, but can also be a server. 84 * Target: A subscriber, or a group of subscribers. A publisher is 85 able to securely dispatch updates to specific targets. Using an 86 HTTP [RFC7230] or HTTPS [RFC2818] URL to identify targets is 87 RECOMMENDED. 89 * Hub: A server that handles subscription requests and distributes 90 the content to subscribers when the corresponding topics have been 91 updated (a Hub implementation is provided in this repository). 92 Any hub MAY implement its own policies on who can use it. 94 2. Discovery 96 If the publisher is a server, it SHOULD advertise the URL of one or 97 more hubs to the subscriber, allowing it to receive live updates when 98 topics are updated. If more than one hub URL is specified, it is 99 expected that the publisher notifies each hub, so the subscriber MAY 100 subscribe to one or more of them. 102 The publisher SHOULD include at least one Link Header [RFC5988] with 103 "rel=mercure" (a hub link header). The target URL of these links 104 MUST be a hub implementing the Mercure protocol. 106 Note: this relation type has not been registered yet [RFC5988]. 107 During the meantime, the relation type "https://git.io/mercure" can 108 be used instead. 110 The publisher MAY provide the following target attributes in the Link 111 headers: 113 * "last-event-id": the globally unique identifier of the last event 114 dispatched by the publisher at the time of the generation of this 115 resource. If provided, it MUST be passed to the hub through a 116 query parameter called "Last-Event-ID" and will be used to ensure 117 that possible updates having been made during between the resource 118 generation time and the connection to the hub are not lost. See 119 section #Re-Connection-and-State-Reconciliation. If this 120 attribute is provided, the publisher MUST always set the "id" 121 parameter when sending updates to the hub. 123 * "content-type": the content type of the updates that will pushed 124 by the hub. If omited, the subscriber MUST assume that the 125 content type will be the same than the one of the original 126 resource. Setting the "content-type" attribute is especially 127 useful to hint that partial updates will be pushed, using formats 128 such as JSON Patch [RFC6902] or JSON Merge Patch [RFC7386]. 130 * "key-set=": the key(s) to decrypt updates encoded in the 131 JWKS (JSON Web Key Set) format (see the Encryption section). 133 All these attributes are optional. 135 The publisher MAY also include one Link Header [RFC5988] with 136 "rel=self" (the self link header). It SHOULD contain the canonical 137 URL for the topic to which subscribers are expected to use for 138 subscriptions. If the Link with "rel=self" is ommitted, the current 139 URL of the resource MUST be used as fallback. 141 Minimal example: 143 GET /books/foo.jsonld HTTP/1.1 144 Host: example.com 146 HTTP/1.1 200 Ok 147 Content-type: application/ld+json 148 Link: ; rel="mercure" 150 {"@id": "/books/foo.jsonld", "foo": "bar"} 152 Links embedded in HTML or XML documents (as defined in the WebSub 153 recommendation) MAY also be supported by subscribers. 155 Note: the discovery mechanism described in this section is strongly 156 inspired from the one specified in the WebSub recommendation 157 (https://www.w3.org/TR/websub/#discovery). 159 3. Subscriptions 161 The subscriber subscribes to an URL exposed by a hub to receive 162 updates of one or many topics. To subscribe to updates, the client 163 opens an HTTPS connection following the Server-Sent Events 164 specification (https://html.spec.whatwg.org/multipage/server-sent- 165 events.html) to the hub's subscription URL advertised by the 166 Publisher. The "GET" HTTP method must be used. The connection 167 SHOULD use HTTP/2 to leverage mutliplexing and other advanced 168 features of this protocol. 170 The subscriber specifies the list of topics to get updates for by 171 using one or several query parameters named "topic". The value of 172 these query parameters MUST be URI templates [RFC6570]. 174 Note: an URL is also a valid URI template. 176 The protocol doesn't specify the maximum number of "topic" parameters 177 that can be sent, but the hub MAY apply an arbitrary limit. 179 The EventSource JavaScript interface 180 (https://html.spec.whatwg.org/multipage/server-sent-events.html#the- 181 eventsource-interface) MAY be used to establish the connection. Any 182 other appropriate mechanism including but not limited to readable 183 streams (https://developer.mozilla.org/en- 184 US/docs/Web/API/Streams_API/Using_readable_streams) and 185 XMLHttpRequest (https://developer.mozilla.org/en- 186 US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest) (used by popular 187 polyfills) MAY also be used. 189 The hub sends updates concerning all subscribed resources matching 190 the provided URI templates and the provided targets (see section 191 #Authorization). If no targets are specied, the update is dispatched 192 to all subscribers. The hub MUST send these updates as text/event- 193 stream compliant events (https://html.spec.whatwg.org/multipage/ 194 server-sent-events.html#sse-processing-model). 196 The "data" property MUST contain the new version of the topic. It 197 can be the full resource, or a partial update by using formats such 198 as JSON Patch "@RFC6902" or JSON Merge Patch "@RFC7386". 200 All other properties defined in the Server-Sent Events specification 201 MAY be used and SHOULD be supported by hubs. 203 The resource SHOULD be represented in a format with hypermedia 204 capabilities such as JSON-LD [W3C.REC-json-ld-20140116], Atom 205 [RFC4287], XML [W3C.REC-xml-20081126] or HTML [W3C.REC- 206 html52-20171214]. 208 Web Linking [RFC5988] SHOULD be used to indicate the IRI of the 209 resource sent in the event. When using Atom, XML or HTML as 210 serialization format for the resource, the document SHOULD contain a 211 "link" element with a "self" relation containing the IRI of the 212 resource. When using JSON-LD, the document SHOULD contain an "@id" 213 property containing the IRI of the resource. 215 Example: 217 // The subscriber subscribes to updates for the https://example.com/foo topic 218 // and to any topic matching https://example.com/books/{name} 219 const url = new URL('https://example.com/hub'); 220 url.searchParams.append('topic', 'https://example.com/foo'); 221 url.searchParams.append('topic', 'https://example.com/bar/{id}'); 223 const eventSource = new EventSource(url); 225 // The callback will be called every time an update is published 226 eventSource.onmessage = function ({data}) { 227 console.log(data); 228 }; 230 The hub MAY require that the subscribers are authorized to receive 231 any update. 233 4. Publication 235 The publisher send updates by issuing "POST" HTTPS requests on the 236 hub URL. When it receives an update, the hub dispatches it to 237 subsribers using the established server-sent events connections. 239 An application CAN send events directly to the subscribers, without 240 using an external hub server, if it is able to do so. In this case, 241 it *MAY NOT* implement the endpoint to publish updates. 243 The request MUST be encoded using the "application/x-www-form- 244 urlencoded" format and contains the following data: 246 * "topic": IRIs of the updated topic. If this key is present 247 several times, the first occurence is considered to be the 248 canonical URL of the topic, and other ones are considered to be 249 alternate URLs. The hub MUST dispatch this update to subscribers 250 subscribed to both canonical or alternate URLs. 252 * "data": the content of the new version of this topic. 254 * "target" (optional): target audience of this update. This key can 255 be present several times. See section #Authorization for further 256 information. 258 * "id" (optional): the topic's revision identifier, it will be used 259 as the SSE's "id" property, if omited the hub MUST generate a 260 valid globally unique id. It "MAY" be an UUID. 262 * "type" (optional): the SSE's "event" property (a specific event 263 type) 265 * "retry" (optional): the SSE's "retry" property (the reconnection 266 time) 268 In case of success, the HTTP response's body MUST be the "id" 269 associated to this update (the one generated by the hub, if it has 270 not been provided by the client) and a success HTTP status code MUST 271 be returned. The publisher MUST be authorized to publish updates. 272 See section #Authorization. 274 5. Authorization 276 To ensure that they are authorized, both publishers and subscribers 277 must present a valid JWS [RFC7515] in compact serialization to the 278 hub. This JWS SHOULD be short lived, especially if the subscriber is 279 a web browser. A different key MAY be used to sign subscribers' and 280 publishers' tokens. 282 Two mechanisms are defined to present the JWS to the hub: 284 * using an "Authorization" HTTP header 286 * using a cookie 288 If a publisher or the subscriber is not a web browser, it SHOULD use 289 an "Authorization" HTTP header. This "Authorization" header MUST 290 contain the string "Bearer" followed by the JWS. The hub will check 291 that the JWS is conform to the rules defined later to ensure that the 292 client is authorized to publish or subscribe to updates. 294 By the "EventSource" specification, web browsers can not set custom 295 HTTP headers for such connections, and they can only be estabilished 296 using the "GET" HTTP method. However, cookies are supported, and can 297 be included even in crossdomain requests if the CORS credentials are 298 set (https://html.spec.whatwg.org/multipage/server-sent- 299 events.html#dom-eventsourceinit-withcredentials): 301 If a publisher or a subscriber is a web browser, it SHOULD send a 302 cookie called "mercureAuthorization" containing the JWS when 303 connecting to the hub. 305 When possible, to improve the overall security, the 306 "mercureAuthorization" cookie SHOULD be set during the discovery. 307 See section #Discovery. Consequently, if the cookie is set during 308 the discovery, both the publisher and the hub have to share the same 309 second level domain. The "Domain" attribute MAY be used to allow the 310 publisher and the hub to use different subdomains. 312 The cookie SHOULD have the "Secure", "HttpOnly" and "SameSite" 313 attributes set. Setting the cookie's "Path" attribute SHOULD also be 314 set to the hub's URL. See section #Security-Considerations. 316 When using authorization mechanisms, the connection MUST use an 317 encryption layer such as HTTPS. 319 If both an "Authorization" HTTP header and a cookie named 320 "mercureAuthorization" are presented by the client, the cookie MUST 321 be ignored. If a client tries to execute an operation it is not 322 allowed to, a 403 HTTP status code SHOULD be returned. 324 5.1. Publishers 326 Publishers MUST be authorized to dispatch updates to the hub, and 327 MUST prove that they are allowed to send updates. 329 To be allowed to publish an update, the JWT presented by the 330 publisher MUST contain a claim called "mercure", and this claim MUST 331 contain a "publish" key. JWT's "mercure.publish" contains an array 332 of targets the publisher is allowed to dispatch updates to. 334 If "mercure.publish": 336 * is not defined, then the publisher MUST NOT be authorized to 337 dispatch any update 339 * contains an empty array, then the publisher is only allowed to 340 dispatch public updates 342 * contains the reserved string "*" as an array value, then the 343 publisher is authorized to dispatch updates to all targets 345 If a topic is not public, the "POST" request sent by the publisher to 346 the hub MUST contain a list of keys named "target". Theirs values 347 MUST be of type "string", and it is RECOMMENDED to use valid IRIs. 348 They can be, for instance a user ID, or a list of group IDs. If an 349 update contains at least one target the publisher is not authorized 350 for, the hub MUST NOT dispatch the update (even if some targets in 351 the list are allowed) and SHOULD return a 403 HTTP status code. 353 5.2. Subscribers 355 Subscribers MAY need to be authorized to connect to the hub. To 356 receive updates destined to specific targets, they MUST be 357 authorized, and MUST prove they belong to at least one of the 358 specified targets. If the subscriber is not authorized it MUST NOT 359 receive any update having at least one target. 361 To receive updates destined to specific targets, the JWS presented by 362 the subscriber MUST have a claim named "mercure" with a key named 363 "subscribe" that contains an array of strings: the list of targets 364 the user is authorized to receive updates for. The targets SHOULD be 365 IRIs. 367 If at least one target is specified, the update MUST NOT be sent to 368 the subscriber by the hub, unless the "mercure.subscribe" property of 369 the JWS presented by the subscriber contains at least one of the 370 specified targets. 372 If the "mercure.subscribe" array contains the reserved string value 373 "*", then the subscriber is authorized to receive updates destined to 374 all targets. 376 6. Re-Connection and State Reconciliation 378 To allow re-establisment in case of connection lost, events 379 dispatched by the hub SHOULD include an "id" property. The value 380 contained in this "id" property SHOULD be a globally unique 381 identifier. To do so, UUID [RFC4122] MAY be used. 383 According to the server-sent events specification, in case of 384 connection lost the subscriber will try to automatically reconnect. 385 During the reconnection the subscriber MUST send the last received 386 event id in a Last-Event-ID (https://html.spec.whatwg.org/multipage/ 387 iana.html#last-event-id) HTTP header. 389 The server-sent events specification doesn't allow to set this HTTP 390 header during the first connection (before a re-connection occurs). 391 In order to fetch any update dispatched between the initial resource 392 generation by the publisher and the connection to he hub, the 393 subscriber MUST send the event id provided during the discovery in 394 the "last-event-id" link's attribute in a query parameter named 395 "Last-Event-ID" when connecting to the hub. 397 If both the "Last-Event-ID" HTTP header and the query parameter are 398 present, the HTTP header MUST take precedence. 400 If the "Last-Event-ID" header or query parameter exists, the hub 401 SHOULD send to the subscriber all events published since the one 402 having this identifier. 404 The hub MAY discard some messages for operational reasons. The 405 subscriber MUST NOT assume that no update will be lost, and MUST re- 406 fetch the original topic to ensure this (for instance, after a long 407 deconnection time). 409 The hub MAY also specify the reconnection time using the "retry" key, 410 as specified in the server-sent events format. 412 7. Encryption 414 Using HTTPS doesn't prevent the hub to access to the update's 415 content. Depending of the intended privacy of informations contained 416 in the updates, it MAY be necessary to prevent eavesdropping by the 417 hub. 419 To make sure that the message content can not be read by the hub, the 420 publisher MAY encode the message before sending it to the hub. The 421 publisher SHOULD use JSON Web Encryption [RFC7516] to encrypt the 422 update content. The publisher MAY provide the relevant encryption 423 key(s) in the "key-set" attribute of the Link HTTP header during the 424 discovery. The "key-set" attribute SHOULD contain a key encoded 425 using the JSON Web Key Set [RFC7517] format. Any other out-of-band 426 mechanism MAY be used instead to share the key between the publisher 427 and the subscriber. 429 Updates encyption is considered a best practice to prevent mass 430 surveillance. This is especially relevant if the hub is managed by 431 an external provider. 433 8. Security Considerations 435 The confidentiality of the secret key(s) used to generate the JWTs is 436 a primary concern. The secret key(s) MUST be stored securely. They 437 MUST be revoked immediatly in case of compromission. 439 Possessing a valid JWTs allows any client to subscribe, or to publish 440 to the hub. Their confidentiality MUST therefore be ensured. To do 441 so, JWTs MUST only be transmited over secure connections. 443 Also, when the client is a web browser, to be resilient to Cross-site 444 Scription (XSS) attacks (https://www.owasp.org/index.php/Cross- 445 site_Scripting_(XSS)), the JWT SHOULD not be made accessible to 446 JavaScript scripts. It's main reason why, when the client is a web 447 browser, using "HttpOnly" cookies as authorization mechanism SHOULD 448 always be prefered. 450 In case of compromission, revoking a JWT before its expiration is 451 often difficult. So, using short-lived token is strongly 452 RECOMMENDED. 454 The publish endpoint of the hub may be targeted by Cross-Site Request 455 Forgery (CSRF) attacks (https://www.owasp.org/index.php/Cross- 456 Site_Request_Forgery_(CSRF)) when the cookie-based authorization 457 mechanism is used. Therefore, implementations supporting this 458 mechanism MUST mitigate such attacks. 460 The first prevention method to implement is to set the 461 "mercureAuthorization" cookie's "SameSite" attribute. However, some 462 web browsers still not support this attribute 463 (https://caniuse.com/#feat=same-site-cookie-attribute), and will stay 464 vulnerable. In addition, hub implementations SHOULD use the "Origin" 465 and "Referer" HTTP headers set by web browsers to verify that the 466 source origin matches the target origin. If none of these headers 467 are available, the hub SHOULD discard the request. 469 CSRF prevention techniques, including the ones previously mentioned, 470 are described in depth in OWASP's Cross-Site Request Forgery (CSRF) 471 Prevention Cheat Sheet (https://www.owasp.org/index.php/Cross- 472 Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet). 474 9. References 476 9.1. Normative References 478 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 479 Requirement Levels", BCP 14, RFC 2119, 480 DOI 10.17487/RFC2119, March 1997, . 483 [RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, 484 DOI 10.17487/RFC2818, May 2000, . 487 [RFC4122] Leach, P., Mealling, M., and R. Salz, "A Universally 488 Unique IDentifier (UUID) URN Namespace", RFC 4122, 489 DOI 10.17487/RFC4122, July 2005, . 492 [RFC5988] Nottingham, M., "Web Linking", RFC 5988, 493 DOI 10.17487/RFC5988, October 2010, . 496 [RFC6570] Gregorio, J., Fielding, R., Hadley, M., Nottingham, M., 497 and D. Orchard, "URI Template", RFC 6570, 498 DOI 10.17487/RFC6570, March 2012, . 501 [RFC7230] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer 502 Protocol (HTTP/1.1): Message Syntax and Routing", 503 RFC 7230, DOI 10.17487/RFC7230, June 2014, 504 . 506 [RFC7515] Jones, M., Bradley, J., and N. Sakimura, "JSON Web 507 Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, May 508 2015, . 510 [RFC7516] Jones, M. and J. Hildebrand, "JSON Web Encryption (JWE)", 511 RFC 7516, DOI 10.17487/RFC7516, May 2015, 512 . 514 [RFC7517] Jones, M., "JSON Web Key (JWK)", RFC 7517, 515 DOI 10.17487/RFC7517, May 2015, . 518 9.2. Informative References 520 [RFC4287] Nottingham, M., Ed. and R. Sayre, Ed., "The Atom 521 Syndication Format", RFC 4287, DOI 10.17487/RFC4287, 522 December 2005, . 524 [RFC6902] Bryan, P., Ed. and M. Nottingham, Ed., "JavaScript Object 525 Notation (JSON) Patch", RFC 6902, DOI 10.17487/RFC6902, 526 April 2013, . 528 [RFC7386] Hoffman, P. and J. Snell, "JSON Merge Patch", RFC 7386, 529 DOI 10.17487/RFC7386, October 2014, . 532 [W3C.REC-html52-20171214] 533 Faulkner, S., Eicholz, A., Leithead, T., Danilo, A., and 534 S. Moon, "HTML 5.2", World Wide Web Consortium 535 Recommendation REC-html52-20171214, 14 December 2017, 536 . 538 [W3C.REC-json-ld-20140116] 539 Sporny, M., Kellogg, G., and M. Lanthaler, "JSON-LD 1.0", 540 World Wide Web Consortium Recommendation REC-json-ld- 541 20140116, 16 January 2014, . 544 [W3C.REC-xml-20081126] 545 Bray, T., Paoli, J., Sperberg-McQueen, M., Maler, E., and 546 F. Yergeau, "Extensible Markup Language (XML) 1.0 (Fifth 547 Edition)", World Wide Web Consortium Recommendation REC- 548 xml-20081126, 26 November 2008, 549 . 551 Author's Address 553 Kevin Dunglas 554 Les-Tilleuls.coop 555 5 rue Hegel 556 Lille 59000 557 France 559 Email: kevin@les-tilleuls.coop