95 lines
4.2 KiB
ReStructuredText
95 lines
4.2 KiB
ReStructuredText
Execution Model
|
|
===============
|
|
This document describes the execution model of the Normandy Client.
|
|
|
|
The basic unit of instruction from the server is a *recipe*, which contains
|
|
instructions for filtering, and arguments for a given action. See below for
|
|
details.
|
|
|
|
One iteration through all of these steps is called a *Normandy session*. This
|
|
happens at least once every 6 hours, and possibly more often if Remote
|
|
Settings syncs new changes.
|
|
|
|
1. Fetching
|
|
-----------
|
|
A list of all active recipes is retrieved from Remote Settings, which has
|
|
likely been syncing them in the background.
|
|
|
|
2. Suitability
|
|
--------------
|
|
|
|
Once recipes have been retrieved, they go through several checks to determine
|
|
their suitability for this client. Recipes contain information about which
|
|
clients should execute the recipe. All recipes are processed by all clients,
|
|
and all filtering happens in the client.
|
|
|
|
For more information, see `the suitabilities docs <./suitabilities.html>`_.
|
|
|
|
Signature
|
|
~~~~~~~~~
|
|
|
|
First, recipes are validated using a signature generated by Autograph_ that
|
|
is included with the recipe. This signature validates both the contents of
|
|
the recipe as well as its source.
|
|
|
|
This signature is separate and distinct from the signing that happens on the
|
|
Remote Settings collection. This provides additional assurance that this
|
|
recipe is legitimate and intended to run on this client.
|
|
|
|
.. _Autograph: https://github.com/mozilla-services/autograph
|
|
|
|
Capabilities
|
|
~~~~~~~~~~~~
|
|
Next a recipe is checked for compatibility using *capabilities*.
|
|
Capabilities are simple strings, such as ``"action:show-heartbeat"``. A
|
|
recipe contains a list of required capabilities, and the Normandy Client has
|
|
a list of capabilities that it supports. If any of the capabilities required
|
|
by the recipe are not compatible with the client, then the recipe does not
|
|
execute.
|
|
|
|
Capabilities are used to avoid running recipes on a client that are so
|
|
incompatible as to be harmful. For example, some changes to filter expression
|
|
handling cannot be detected by filter expressions, and so older clients that
|
|
receive filters using these new features would break.
|
|
|
|
.. note::
|
|
|
|
Capabilities were first introduced in Firefox 70. Clients prior to this
|
|
do not check capabilities, and run all recipes provided. To accommodate
|
|
this, the server splits recipes into two Remote Settings collections,
|
|
``normandy-recipes``, and ``normandy-recipes-capabilities``. Clients
|
|
prior to Firefox 70 use the former, whereas Firefox 70 and above use the
|
|
latter. Recipes that only require "baseline" capabilities are published
|
|
to both, and those that require advanced capabilities are only published
|
|
to the capabilities aware collection.
|
|
|
|
Filter Expressions
|
|
~~~~~~~~~~~~~~~~~~
|
|
Finally the recipe's filter expression is checked. Filter expressions are
|
|
written in an expression language named JEXL_ that is similar to JavaScript,
|
|
but far simpler. It is intended to be as safe to evaluate as possible.
|
|
|
|
.. _JEXL: https://github.com/mozilla/mozjexl
|
|
|
|
Filters are evaluated in a context that contains details about the client
|
|
including browser versions, installed add-ons, and Telemetry data. Filters
|
|
have access to "transforms" which are simple functions that can do things like
|
|
check preference values or parse strings into ``Date`` objects. Filters don't
|
|
have access to change any state in the browser, and are generally
|
|
idempotent. However, filters are *not* considered to be "pure functions",
|
|
because they have access to state that may change, such as time and location.
|
|
|
|
3. Execution
|
|
------------
|
|
After a recipe's suitability is determined, that recipe is executed. The
|
|
recipe specifies an *action* by name, as well as arguments to pass to that
|
|
action. The arguments are validated against an expected schema.
|
|
|
|
All action have a pre- and post-step that runs once each Normandy session.
|
|
The pre-step is run before any recipes are executed, and once the post-step
|
|
is executed, no more recipes will be executed on that action in this session.
|
|
|
|
Each recipe is passed to the action, along with its suitability. Individual
|
|
actions have their own semantics about what to do with recipes. Many actions
|
|
maintain their own life cycle of events for new recipes, existing recipes,
|
|
and recipes that stop applying to this client.
|