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`).