signatu policy engine
npm install @signatu/policyengineThe goal of the new engine is to build a robust, safe, extensible and
maintainable Policy engine that can support multiple languages and output
formats.
The new engine will be built using TypeScript to leverage compile-time
type safety. The engine will be built to be run either on the server under
node.js, or in the browser.
A diagram is maintained at Draw.io
Policy is a representation of the Privacy Policy theQuestions. The answers will result in one or more PolicyClause, of the following type:* Pre-defined static clauses
* Pre-defined clauses from a mutually exclusive list, including possible "specify..." input.
* Pre-defined clauses from multiple selections from a list, including possible "specify..." input.
* Free-text input
A PolicySection represents a section that considers a certain topic, e.g., "Transfer of Data". Both PolicySection and PolicyClause implements the PolicyElement interface. A Policy is hence an ordered list of objects that implement PolicyElement.
A PolicyGenerator will generate a PolicyDocument from a given Policy. Typically there will be different PolicyGenerator implementations for the different output formats, and possibly languages.
The user-specified input will as a rule not be provided in a controlled English, hence it may require translation to allow for output in other formats.
1. Each question can result in different clauses depending on the option the user chooses
1. Clauses can be output in different languages and formats (e.g., pictures)
1. Clauses can include user-specified input texts, written in a non-controlled, natural language (e.g., German).
PolicyElement corresponds to a specific option for a Question, referencing user-specified input through a UserInput object. A PolicyElement will have a set of PolicyContent objects associated with each option, one for each language and potentially other types as well (e.g., pictures).Questionnaire is structured as an ordered list of Sections, who all have an ordered list of Questions. Each Question option has a single PolicyElement associated with it, specifically a PolicyClause. The masterText is the controlled English for the clause.To add actual content to the PolicyElement, one or more PolicyContent is added to the PolicyElement. For example, a TextContent object having language: en can be added to make an English plaintext version of the clause.
#### Answer a questionnaire”
Each Question is presented to the user. When a particular option is chosen, it is connected to a particular PolicyElement, and the PolicyElement is added to the Policy. If the PolicyElement requiresInput, then collect input from the user, and add that to the Policy as well. The UserInput will reference the PolicyElement it contains input for. This will be used by the PolicyGenerator.
#### Generate a policy
A PolicyGenerator will create output based on a Policy and some parameters, such as the language, type of output (text, pictures, etc) and so on.
The generation is pretty straightforward:
1. For each PolicyElement:
* Get the correct associated PolicyContent object. When generating a text-based policy in English, for example, the right object would be a TextContent having language: en.
* If the PolicyContent requires/has input, get the associated UserInput object and provide the generate() method.
2. Combine the output of each PolicyElement in sequence into the final PolicyDocument.
3. Repeat for different parameters (e.g., language).
#### User input
Some questions require the user to "specify" further - e.g., the type of processing, the retainment time for data, and so on. This input is merged into the PolicyContent from PolicyElement. Note that a PolicyElement can have multiple PolicyContent - different languages, different formats (icon, speech, video), so we need to be able to track multiple UserInput for each PolicyElement.
We want the PolicyElement and PolicyContent objects to be the same across different Policies, so the current thinking is to provide UserInput[] to a PolicyContent.generate() function.
We'll use Mustache to render user input. All UserInput objects have a given name, and that name is substituted for {{name}} in the PolicyContent text.
#### Metadata
Some of the PolicyContent are template strings - e.g., "{{controller.name}} processes data in the US". The meta data may be in different languages and formats, too. Meta-data is also handled with Mustache, where the Policy metaData is passed. If the text contains e.g., {{controller.name}}, then we expect meta data to have an object controller with a property name.
#### Generate a clause for an external service
E.g., Google Analytics. This is equivalent to the others - an ExternalService object (which implements PolicyElement) is added to the Policy. The appropriate PolicyContent objects are added to the ExternalService - e.g., IconContent .
PolicyDocuments containing the same PolicyElement can be said to be legally equivalent for that particular area, and that the legal consequences are similar. This may be difficult in practice, but if possible, it would ease reasoning over policies, finding similar policies and so on.UserInput just be a class implementing PolicyContent?generate() method be moved out of the PolicyContent interface? How do we control generation to different formats - e.g., plain text, HTML, JSON, XML, PNG, etc?PolicySection and PolicyClause? Can't both of them just be a PolicyElement?UserInputPolicyElement for each Policy? => associate it with both the Policy and the PolicyElement (one way only). Hence when generating a policy, we keep track of the input for all the PolicyElements.