github.com/cozy/cozy-stack@v0.0.0-20240327093429-939e4a21320e/docs/apps.md (about) 1 [Table of contents](README.md#table-of-contents) 2 3 # Applications 4 5 It's possible to manage serverless applications from the cozy stack and serve 6 them via cozy stack. The stack does the routing and serve the HTML and the 7 assets for the applications. The assets of the applications are installed in the 8 virtual file system. 9 10 ## Install an application 11 12 ### The manifest 13 14 To install an application, cozy needs a manifest. It's a JSON document that 15 describes the application (its name and icon for example), how to install it and 16 what it needs for its usage (the permissions in particular). While we have 17 considered to use the same 18 [manifest format as the W3C for PWAs](https://www.w3.org/TR/appmanifest/), it 19 didn't match our expectations. The 20 [manifest format for FirefoxOS](https://developer.mozilla.org/en-US/docs/Archive/Firefox_OS/Firefox_OS_apps/Building_apps_for_Firefox_OS/Manifest) 21 is a better fit. We took a lot of inspirations from it, starting with the 22 filename for this file: `manifest.webapp`. 23 24 | Field | Description | 25 | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | 26 | name | the name to display on the home | 27 | name_prefix | the prefix to display with the name | 28 | slug | the default slug (it can be changed at install time) | 29 | editor | the editor's name to display on the cozy-bar of the app | 30 | icon | an icon for the home | 31 | screenshots | an array of path to the screenshots of the application | 32 | category | the category of the application | 33 | short_description | a short description of the application | 34 | long_description | a long description of the application | 35 | source | where the files of the app can be downloaded | 36 | developer | `name` and `url` for the developer | 37 | locales | translations of the name and description fields in other locales | 38 | langs | list of languages tags supported by the application | 39 | version | the current version number | 40 | license | [the SPDX license identifier](https://spdx.org/licenses/) | 41 | platforms | a list of `type`, `url` values for derivate of the application for other devices | 42 | intents | a list of intents provided by this app (see [here](intents.md) for more details) | 43 | permissions | a map of permissions needed by the app (see [here](permissions.md) for more details) | 44 | notifications | a map of notifications needed by the app (see [here](notifications.md) for more details) | 45 | services | a map of the services associated with the app (see below for more details) | 46 | routes | a map of routes for the app (see below for more details) | 47 | mobile | information about app's mobile version (see below for more details) | 48 | accept_from_flagship | boolean stating if the app is compatible with the Flagship app's "OS Receive" feature | 49 | accept_documents_from_flagship | when `accept_from_flagship` is `true`, defines what can be uploaded to the app (see [here](accept-from-flagship.md) for more details) | 50 51 ### Routes 52 53 A route make the mapping between the requested paths and the files. It can have 54 an index, which is an HTML file, with a token injected on it that identify the 55 application. This token must be used with the user cookies to use the services 56 of the cozy-stack (except in the cases of a shared by link page). 57 58 By default, a route can be only visited by the authenticated owner of the 59 instance where the app is installed. But a route can be marked as public. In 60 that case, anybody can visit the route. 61 62 For example, an application can offer an administration interface on `/admin`, a 63 public page on `/public`, and shared assets in `/assets`: 64 65 ```json 66 { 67 "/admin": { 68 "folder": "/", 69 "index": "admin.html", 70 "public": false 71 }, 72 "/public": { 73 "folder": "/public", 74 "index": "index.html", 75 "public": true 76 }, 77 "/assets": { 78 "folder": "/public-assets", 79 "public": true 80 } 81 } 82 ``` 83 84 If an application has no routes in its manifest, the stack will create one 85 route, this default one: 86 87 ```json 88 { 89 "/": { 90 "folder": "/", 91 "index": "index.html", 92 "public": false 93 } 94 } 95 ``` 96 97 **Note**: if you have a public route, it's probably better to put the app icon 98 in it. So, the cozy-bar can display it for the users that go on the public part 99 of the app. 100 101 When the stack receives a request, it chooses the more specific route that 102 matches, then it looks inside the associated folder to find the file, and send 103 it. If the route is an exact match, the file will be the index, and it will be 104 interpreted as a template, cf [the list of 105 variables](https://docs.cozy.io/en/cozy-stack/client-app-dev/#good-practices-for-your-application). 106 107 For example, with the previous routes, a request to `/admin` will use the file 108 `/admin.html` as a template for the response. And a request to 109 `/assets/css/theme.css` will use the file `/public-assets/css/theme.css`. 110 111 ### Services 112 113 Application may require background and offline process to analyse the user's 114 data and emit some notification or warning even without the user being on the 115 application. These part of the application are called services and can be 116 declared as part of the application in its manifest. 117 118 In contrast to [konnectors](./konnectors.md), services have the same permissions 119 as the web application and are not intended to collect outside informations but 120 rather analyse the current set of collected information inside the cozy. However 121 they share the same mechanisms as the konnectors to describe how and when they 122 should be executed: via our trigger system. 123 124 To define a service, first the code needs to be stored with the application 125 content, as single (packaged) javascript files. In the manifest, declare the 126 service and its parameters following this example: 127 128 ```json 129 { 130 "services": { 131 "low-budget-notification": { 132 "type": "node", 133 "file": "/services/low-budget-notification.js", 134 "trigger": "@cron 0 0 0 * * *" 135 } 136 // ... 137 } 138 } 139 ``` 140 141 The `trigger` field should follow the available triggers described in the 142 [jobs documentation](./jobs.md). The `file` field should specify the service 143 code run and the `type` field describe the code type (only `"node"` for now). 144 145 If you need to know more about how to develop a service, please check the 146 [how-to documentation here](https://github.com/cozy/cozy.github.io/blob/dev/src/howTos/dev/services.md). 147 148 **Note:** it is possible to declare a service with no trigger. For example, 149 this can be used when the service is programmatically called from another 150 service. 151 152 ### Available fields to the service 153 During the service execution, the stack will give some environment variables to the service if you need to use them, available with `process.env[FIELD]`. Once again, it's the **stack** that gives those variables. So if you're developing a service and using a script to execute/test your service, you won't get those variables. 154 155 ```bash 156 - "COZY_URL" # Cozy URL 157 - "COZY_CREDENTIALS" # The cozy app related token 158 - "COZY_LANGUAGE" # Lang used for the service (ex: node) 159 - "COZY_LOCALE" # Locale of the Cozy 160 - "COZY_TIME_LIMIT" # Maximum execution time. After this, the job will be killed 161 - "COZY_JOB_ID" # Job ID 162 - "COZY_COUCH_DOC" # The CouchDB document which triggers the service 163 ``` 164 ### Notifications 165 166 For more informations on how te declare notifications in the manifest, see the 167 [notifications documentation](./notifications.md). 168 169 Here is an example: 170 171 ```json 172 { 173 "notifications": { 174 "account-balance": { 175 "description": "Alert the user when its account balance is negative", 176 "collapsible": true, // only interested in the last value of the notification 177 "multiple": true, // require sub-categories for each account 178 "stateful": false, 179 "default_priority": "high", // high priority for this notification 180 "templates": { 181 "mail": "file:./notifications/account-balance-mail.tpl" 182 } 183 } 184 } 185 } 186 ``` 187 188 ### Mobile 189 190 Application may exist on mobile platforms. When it is the case, manifest can 191 contain informations about the mobile apps. 192 193 On a mobile device and on a native context, this attribute can be used to open the 194 native mobile app from any another Cozy app. 195 196 Following attributes should be set: 197 - `schema`: the app's scheme that can be used to open it 198 - `id_playstore`: the app's ID on the Google PlayStore 199 - `id_appstore`: the app's ID on the Apple AppStore 200 201 Example of `mobile` attribute for Cozy Pass: 202 ```json 203 "mobile": { 204 "schema": "cozypass://", 205 "id_playstore": "io.cozy.pass", 206 "id_appstore": "cozy-pass/id1502262449" 207 } 208 ``` 209 210 ## Resource caching 211 212 To help caching of applications assets, we detect the presence of a unique 213 identifier in the name of assets: a unique identifier is matched when the file 214 base name contains a long hexadecimal subpart between '.', of at least 10 215 characters. For instance `app.badf00dbadf00d.js` or `icon.badbeefbadbeef.1.png`. 216 217 With such a unique identifier, the asset is considered immutable, and a long 218 cache-control is added on corresponding HTTP responses. 219 220 We recommend the use of bundling tools like 221 [webpack](https://webpack.github.io/) which offer the possibility to add such 222 identifier on the building step of the application packages for all assets. 223 224 ## Sources 225 226 Here is the available sources, defined by the scheme of the source URL: 227 228 - `registry://`: to install an application from the instance registries 229 - `git://`, `git+ssh://`, or `git+https://`: to install an application from a git repository 230 - `http://` or `https://`: to install an application from an http server (via 231 a tarball) 232 - `file://`: to install an application from a local directory (for instance: 233 `file:///home/user/code/cozy-app`) 234 235 The `registry` scheme expect the following elements: 236 237 - scheme: `registry` 238 - host: the name of the application 239 - path: `/:channel` the channel of the application (see the 240 [registry](./registry.md) doc) 241 242 Examples: `registry://drive/stable`, `registry://drive/beta`, and 243 `registry://drive/dev`. 244 245 For the `git` scheme, the fragment in the URL can be used to specify which 246 branch to install. 247 248 For the `http` and `https` schemes, the fragment can be used to give the 249 expected sha256sum. 250 251 ### POST /apps/:slug 252 253 Install an application, ie download the files and put them in `/apps/:slug` in 254 the virtual file system of the user, create an `io.cozy.apps` document, register 255 the permissions, etc. 256 257 This endpoint is asynchronous and returns a successful return as soon as the 258 application installation has started, meaning we have successfully reached the 259 manifest and started to fetch application data. 260 261 To make this endpoint synchronous, use the header `Accept: text/event-stream`. 262 This will make a eventsource stream sending the manifest and returning when the 263 application has been installed or failed. 264 265 #### Status codes 266 267 - 202 Accepted, when the application installation has been accepted. 268 - 400 Bad-Request, when the manifest of the application could not be processed 269 (for instance, it is not valid JSON). 270 - 404 Not Found, when the manifest or the source of the application is not 271 reachable. 272 - 422 Unprocessable Entity, when the sent data is invalid (for example, the 273 slug is invalid or the Source parameter is not a proper or supported url) 274 275 #### Query-String 276 277 | Parameter | Description | 278 | --------- | ----------------------------------------------------------- | 279 | Source | URL from where the app can be downloaded (only for install) | 280 281 The Source parameter is optional: by default, the stable channel of the 282 registry will be used. 283 284 #### Request 285 286 ```http 287 POST /apps/emails?Source=git://github.com/cozy/cozy-emails.git HTTP/1.1 288 Accept: application/vnd.api+json 289 ``` 290 291 #### Response 292 293 ```http 294 HTTP/1.1 202 Accepted 295 Content-Type: application/vnd.api+json 296 ``` 297 298 ```json 299 { 300 "data": [{ 301 "id": "4cfbd8be-8968-11e6-9708-ef55b7c20863", 302 "type": "io.cozy.apps", 303 "meta": { 304 "rev": "1-7a1f918147df94580c92b47275e4604a" 305 }, 306 "attributes": { 307 "name": "calendar", 308 "state": "installing", 309 "slug": "calendar", 310 ... 311 }, 312 "links": { 313 "self": "/apps/calendar" 314 } 315 }] 316 } 317 ``` 318 319 **Note**: it's possible to choose a git branch by passing it in the fragment 320 like this: 321 322 ```http 323 POST /apps/emails-dev?Source=git://github.com/cozy/cozy-emails.git%23dev HTTP/1.1 324 ``` 325 326 ### PUT /apps/:slug 327 328 Update an application with the specified slug name. 329 330 This endpoint is asynchronous and returns a successful return as soon as the 331 application installation has started, meaning we have successfully reached the 332 manifest and started to fetch application data. 333 334 To make this endpoint synchronous, use the header `Accept: text/event-stream`. 335 This will make a eventsource stream sending the manifest and returning when the 336 application has been updated or failed. 337 338 #### Request 339 340 ```http 341 PUT /apps/emails HTTP/1.1 342 Accept: application/vnd.api+json 343 ``` 344 345 #### Response 346 347 ```http 348 HTTP/1.1 202 Accepted 349 Content-Type: application/vnd.api+json 350 ``` 351 352 ```json 353 { 354 "data": [{ 355 "id": "4cfbd8be-8968-11e6-9708-ef55b7c20863", 356 "type": "io.cozy.apps", 357 "meta": { 358 "rev": "1-7a1f918147df94580c92b47275e4604a" 359 }, 360 "attributes": { 361 "name": "calendar", 362 "state": "installing", 363 "slug": "calendar", 364 ... 365 }, 366 "links": { 367 "self": "/apps/calendar" 368 } 369 }] 370 } 371 ``` 372 373 #### Status codes 374 375 - 202 Accepted, when the application installation has been accepted. 376 - 400 Bad-Request, when the manifest of the application could not be processed 377 (for instance, it is not valid JSON). 378 - 404 Not Found, when the application with the specified slug was not found or 379 when the manifest or the source of the application is not reachable. 380 - 422 Unprocessable Entity, when the sent data is invalid (for example, the 381 slug is invalid or the Source parameter is not a proper or supported url) 382 383 #### Advanced usage 384 385 Two optional query parameters are available for an app update: 386 387 - `PermissionsAcked`: (defaults to `false`) 388 - Tells that the user accepted the permissions/ToS. It is useful if there are 389 newer permissions or Terms Of Service and you want to be sure they were read 390 or accepted. If set to `false`, the update will be blocked and the user will 391 be told that a new app version is available. 392 393 Note: `PermissionsAcked` can be skipped. If an instance is in a `context` 394 configured with the parameter `permissions_skip_verification` sets to 395 `true`, permissions verification will be ignored. 396 397 - `Source` (defaults to `SourceURL` installation parameter): 398 - Use a different source to update this app (e.g. to install a `beta` or `dev` 399 app version) 400 401 ##### Examples: 402 403 - You have an email application on a `stable` channel, and you want to update it 404 to a particular `beta` version: 405 406 ```http 407 PUT /apps/emails?Source=registry://drive/1.0.0-beta HTTP/1.1 408 Accept: application/vnd.api+json 409 ``` 410 411 - You want to attempt the email app update, but prevent it if new permissions 412 were added 413 414 ```http 415 PUT /apps/emails?PermissionsAcked=false HTTP/1.1 416 Accept: application/vnd.api+json 417 ``` 418 419 You can combine these parameters to use a precise app version and stay on 420 another channel (when permissions are different): 421 422 - Install a version (e.g. `1.0.0`) 423 - Ask an update to `stable` channel with `PermissionsAcked` to `false` 424 - `Source` will be `stable`, and your version remains `1.0.0` 425 426 ## List installed applications 427 428 ### GET /apps/ 429 430 An application can be in one of these states: 431 432 - `installed`, the application is installed but still require some user 433 interaction to accept its permissions 434 - `ready`, the user can use it 435 - `installing`, the installation is running and the app will soon be usable 436 - `upgrading`, a new version is being installed 437 - `errored`, the app is in an error state and can not be used. 438 439 #### Request 440 441 ```http 442 GET /apps/ HTTP/1.1 443 Accept: application/vnd.api+json 444 ``` 445 446 #### Response 447 448 ```http 449 HTTP/1.1 200 OK 450 Content-Type: application/vnd.api+json 451 ``` 452 453 ```json 454 { 455 "data": [{ 456 "id": "4cfbd8be-8968-11e6-9708-ef55b7c20863", 457 "type": "io.cozy.apps", 458 "meta": { 459 "rev": "2-bbfb0fc32dfcdb5333b28934f195b96a" 460 }, 461 "attributes": { 462 "name": "calendar", 463 "state": "ready", 464 "slug": "calendar", 465 ... 466 }, 467 "links": { 468 "self": "/apps/calendar", 469 "icon": "/apps/calendar/icon", 470 "related": "https://calendar.alice.example.com/" 471 } 472 }], 473 "links": {}, 474 "meta": { 475 "count": 1 476 } 477 } 478 ``` 479 480 This endpoint is paginated, default limit is currently `100`. 481 Two flags are available to retreieve the other apps if there are more than `100` 482 apps installed: 483 - `limit` 484 - `start_key`: The first following doc ID of the next apps 485 486 The `links` object contains a `ǹext` generated-link for the next docs. 487 488 #### Request 489 490 ```http 491 GET /apps/?limit=50 HTTP/1.1 492 Accept: application/vnd.api+json 493 ``` 494 495 #### Response 496 497 ```http 498 HTTP/1.1 200 OK 499 Content-Type: application/vnd.api+json 500 ``` 501 502 ```json 503 { 504 "data": [{ 505 "id": "4cfbd8be-8968-11e6-9708-ef55b7c20863", 506 "type": "io.cozy.apps", 507 "meta": { 508 "rev": "2-bbfb0fc32dfcdb5333b28934f195b96a" 509 }, 510 "attributes": { 511 "name": "calendar", 512 "state": "ready", 513 "slug": "calendar", 514 ... 515 }, 516 "links": { 517 "self": "/apps/calendar", 518 "icon": "/apps/calendar/icon", 519 "related": "https://calendar.alice.example.com/" 520 } 521 }, {...}], 522 "links": { 523 "next": "http://alice.example.com/apps/?limit=50&start_key=io.cozy.apps%2Fhome" 524 }, 525 "meta": { 526 "count": 50 527 } 528 } 529 ``` 530 531 ## Get informations about an application 532 533 ### GET /apps/:slug 534 535 This route is used to retrieve informations about an application installed on the cozy. By calling this route, the application will be updated synchronously. 536 537 #### Request 538 539 ```http 540 GET /apps/calendar HTTP/1.1 541 Accept: application/vnd.api+json 542 ``` 543 544 #### Response 545 546 ```http 547 HTTP/1.1 200 OK 548 Content-Type: application/vnd.api+json 549 ``` 550 551 ```json 552 { 553 "data": { 554 "id": "4cfbd8be-8968-11e6-9708-ef55b7c20863", 555 "type": "io.cozy.apps", 556 "meta": { 557 "rev": "2-bbfb0fc32dfcdb5333b28934f195b96a" 558 }, 559 "attributes": { 560 "name": "calendar", 561 "state": "ready", 562 "slug": "calendar", 563 ... 564 }, 565 "links": { 566 "self": "/apps/calendar", 567 "icon": "/apps/calendar/icon", 568 "related": "https://calendar.alice.example.com/" 569 } 570 } 571 } 572 ``` 573 574 ## Get the icon of an application 575 576 ### GET /apps/:slug/icon 577 578 #### Request 579 580 ```http 581 GET /apps/calendar/icon HTTP/1.1 582 ``` 583 584 #### Response 585 586 ```http 587 HTTP/1.1 200 OK 588 Content-Type: image/svg+xml 589 ``` 590 591 ```svg 592 <svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60"> 593 <title>Calendar</title> 594 <path fill="#fff" fill-opacity=".011" d="M30 58.75c15.878 0 28.75-12.872 28.75-28.75s-12.872-28.75-28.75-28.75-28.75 12.872-28.75 28.75 12.872 28.75 28.75 28.75zm0 1.25c-16.569 0-30-13.432-30-30 0-16.569 13.431-30 30-30 16.568 0 30 13.431 30 30 0 16.568-13.432 30-30 30z"/> 595 <path d="M47.997 9h-35.993c-1.654 0-3.004 1.345-3.004 3.004v35.993c0 1.653 1.345 3.004 3.004 3.004h35.993c1.653 0 3.004-1.345 3.004-3.004v-35.993c0-1.654-1.345-3.004-3.004-3.004zm-35.997 3.035h4.148v2.257c0 .856.7 1.556 1.556 1.556s1.556-.7 1.556-1.556v-2.257h5.136v2.257c0 .856.7 1.556 1.556 1.556s1.556-.7 1.556-1.556v-2.257h5.137v2.257c0 .856.699 1.556 1.556 1.556s1.556-.7 1.556-1.556v-2.257h5.137v2.257c0 .856.699 1.556 1.556 1.556s1.556-.7 1.556-1.556v-2.257h3.992v6.965h-35.998v-6.965zm36 35.965h-36v-27h36v27zm-21.71-10.15c-.433.34-.997.51-1.69.51-.64 0-1.207-.137-1.7-.409-.493-.273-.933-.603-1.32-.99l-1.1 1.479c.453.508 1.027.934 1.72 1.28.693.347 1.56.521 2.6.521.613 0 1.19-.083 1.73-.25.54-.167 1.013-.407 1.42-.721.407-.312.727-.696.96-1.149.233-.453.35-.974.35-1.56 0-.841-.25-1.527-.75-2.061s-1.13-.9-1.89-1.1v-.08c.693-.268 1.237-.641 1.63-1.12.393-.48.59-1.08.59-1.8 0-.533-.1-1.01-.3-1.43-.2-.42-.48-.772-.84-1.06-.36-.287-.793-.503-1.3-.65-.507-.147-1.067-.22-1.68-.22-.76 0-1.45.147-2.07.44s-1.203.68-1.75 1.16l1.18 1.42c.387-.36.783-.65 1.19-.87.407-.22.863-.33 1.37-.33.587 0 1.047.15 1.38.45.333.3.5.717.5 1.25 0 .293-.057.566-.17.819-.113.253-.3.47-.56.65-.26.18-.6.319-1.02.42-.42.1-.943.149-1.57.149v1.681c.72 0 1.32.05 1.8.149.48.101.863.243 1.15.43.287.188.49.414.61.681.12.267.18.567.18.899 0 .602-.217 1.072-.65 1.412zm13.71.15h-3v-11h-2c-.4.24-.65.723-1.109.89-.461.167-1.25.49-1.891.61v1.5h3v8h-3v2h8v-2z"/> 596 </svg> 597 ``` 598 599 ## Download the code of an app 600 601 This endpoint is used by the flagship app to download the code of an 602 application, in order to use it even while offline. 603 604 ### GET /apps/:slug/download & GET /apps/:slug/download/:version 605 606 The first route will download a tarball of the source code of the latest 607 installed version of the application. The second route will force a specific 608 version of an app (and a 412 Precondition failed may be sent if the code of 609 this specific version is not available). 610 611 #### Request 612 613 ```http 614 GET /apps/drive/download/3.0.1 HTTP/1.1 615 Authorization: Bearer flagship-token 616 Host: cozy.example.net 617 ``` 618 619 #### Response 620 621 ```http 622 HTTP/1.1 200 OK 623 Content-Type: application/gzip 624 ``` 625 626 When the application has been installed from the registry, the stack will 627 respond with a redirect to the registry. In that case, the downloaded tarball 628 can be gzipped or not (the registry allows both). When the application is 629 installed from another source, the stack will create a gzipped tarball and 630 send it to the client. 631 632 ## Open an application inside the flagship app 633 634 This endpoint can be used by the flagship app to get all the parameters needed 635 to open a webview for a Cozy webapp. It includes a cookie for a session, and 636 the values for filling the HTML template. 637 638 ### GET /apps/:slug/open 639 640 #### Request 641 642 ```http 643 GET /apps/drive/open HTTP/1.1 644 Accept: application/vnd.api+json 645 Authorization: Bearer flagship-token 646 Host: cozy.example.net 647 ``` 648 649 **Note:** it is possible to send a cookie in HTTP headers to use the 650 corresponding session when opening the webapp. 651 652 #### Response 653 654 ```http 655 HTTP/1.1 200 OK 656 Content-Type: application/vnd.api+json 657 ``` 658 659 ```json 660 { 661 "data": { 662 "id": "da903430-af6e-013a-8b14-18c04daba326", 663 "type": "io.cozy.apps.open", 664 "attributes": { 665 "Cookie": "sess-cozyfa713dc4b104110f181959012ef2e0c5=AAAAAGJ1GI8zZDZlNTJhZTgxNThhY2VlMjhhMjE1NTY3ZTA1OTYyOCZ9ShFEsLD-2cb9bUdvYSg91XRw919a5oK1VxfshaZB; Path=/; Domain=example.net; HttpOnly; Secure; SameSite=Lax", 666 "AppEditor": "Cozy", 667 "AppName": "Drive", 668 "AppSlug": "drive", 669 "Capabilities": "{ \"file_versioning\": true }", 670 "CozyBar": "...", 671 "CozyClientJS": "...", 672 "CozyFonts": "...", 673 "DefaultWallpaper": "...", 674 "Domain": "cozy.example.net", 675 "Favicon": "...", 676 "Flags": "{}", 677 "IconPath": "icon.svg", 678 "Locale": "en", 679 "SubDomain": "flat", 680 "ThemeCSS": "...", 681 "Token": "eyJhb...Y4KdA", 682 "Tracking": "false" 683 }, 684 "links": { 685 "self": "/apps/drive/open" 686 } 687 } 688 } 689 ``` 690 691 ## Uninstall an application 692 693 ### DELETE /apps/:slug 694 695 #### Request 696 697 ```http 698 DELETE /apps/tasky HTTP/1.1 699 ``` 700 701 #### Response 702 703 ```http 704 HTTP/1.1 204 No Content 705 ``` 706 707 ## Send application logs to cozy-stack 708 709 ### POST /apps/:slug/logs 710 711 Send client-side logs to cozy-stack so they can be stored in the server's 712 logging system. 713 714 The job identifier must be sent in the `job_id` parameter of the query string. 715 The version of the application can be sent in the `version` parameter. 716 717 #### Status codes 718 719 - 204 No Content, when all the log lines have been processed. 720 - 400 Bad-Request, when the JSON body is invalid. 721 - 404 Not Found, when no apps with the given slug could be found. 722 - 422 Unprocessable Entity, when the sent data is invalid (for example, the 723 slug is invalid or log level does not exist) 724 725 #### Request 726 727 ```http 728 POST /apps/emails/logs?job_id=1c6ff5a07eb7013bf7e0-18c04daba326 HTTP/1.1 729 Accept: application/vnd.api+json 730 ``` 731 732 ```json 733 [ 734 { "timestamp": "2022-10-27T17:13:37.293Z", "level": "info", "msg": "Fetching e-mail data..." }, 735 { "timestamp": "2022-10-27T17:13:38.382Z", "level": "error", "msg": "Could not find requested e-mail" } 736 ] 737 ``` 738 739 #### Response 740 741 ```http 742 HTTP/1.1 204 No Content 743 ``` 744 745 ## Access an application 746 747 Each application will run on its sub-domain. The sub-domain is the slug used 748 when installing the application (`calendar.cozy.example.org` if it was installed 749 via a POST on /apps/calendar). On the main domain (`cozy.example.org`), there 750 will be the registration process, the login form, and it will redirect to 751 `home.cozy.example.org` for logged-in users. 752 753 ### Rationale 754 755 The applications have different roles and permissions. An application is 756 identified when talking to the stack via a token. The token has to be injected 757 in the application, one way or another, and it have to be unaccessible from 758 other apps. 759 760 If the applications run on the same domain (for example 761 `https://cozy.example.org/apps/calendar`), it's nearly impossible to protect an 762 application to take the token of another application. The security model of the 763 web is based too heavily on 764 [Same Origin Policy](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) 765 for that. If the token is put in the html of the index.html page of an app, 766 another app can use the fetch API to get the html page, parse its content and 767 extract the token. We can think of other ways to inject the token (indexeddb, 768 URL, cookies) but none will provide a better isolation. Maybe in a couple years, 769 when [the origin spec](https://w3c.github.io/webappsec-suborigins/) will be more 770 advanced. 771 772 So, if having the apps on the same origin is not possible, we have to put them 773 on several origins. One interesting way to do that is using sandboxed iframes. 774 An iframe with 775 [the sandbox attribute](https://www.w3.org/TR/html5/embedded-content-0.html#attr-iframe-sandbox), 776 and not `allow-same-origin` in it, will be assigned to a unique origin. The W3C 777 warns: 778 779 > Potentially hostile files should not be served from the same server as the 780 > file containing the iframe element. Sandboxing hostile content is of minimal 781 > help if an attacker can convince the user to just visit the hostile content 782 > directly, rather than in the iframe. To limit the damage that can be caused by 783 > hostile HTML content, it should be served from a separate dedicated domain. 784 > Using a different domain ensures that scripts in the files are unable to 785 > attack the site, even if the user is tricked into visiting those pages 786 > directly, without the protection of the sandbox attribute. 787 788 It may be possible to disable all html pages to have an html content-type, 789 except the home, and having the home loading the apps in a sandboxed iframe, via 790 the `srcdoc` attribute. But, it will mean that we will have to reinvent nearly 791 everything. Even showing an image can no longer be done via an `<img>` tag, it 792 will need to use post-message with the home. Such a solution is difficult to 793 implement, is a very fragile (both for the apps developer than for security) and 794 is an hell to debug when it breaks. Clearly, it's not an acceptable solution. 795 796 Thus, the only choice is to have several origins, and sub-domains is the best 797 way for that. Of course, it has the downside to be more complicated to deploy 798 (DNS and TLS certificates). But, in the tradeoff between security and ease of 799 administration, we definetively take the security first. 800 801 ### Routes 802 803 > Should we be concerned that all the routes are on the same sub-domain? 804 805 No, it's not an issue. There are two types of routes: the ones that are publics 806 and those reserved to the authenticated user. Public routes have no token 807 808 Private routes are private, they can be accessed only with a valid session 809 cookie, ie by the owner of the instance. Another application can't use the user 810 cookies to read the token, because of the restrictions of the same origin policy 811 (they are on different domains). And the application can't use an open proxy to 812 read the private route, because it doesn't have the user cookies for that (the 813 cookie is marked as `httpOnly`).