diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..daa30a3f752706bce3bc36b34a7aab39ee5484d4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+README.html
diff --git a/README.md b/README.md
index 614be9eb1ee99b11d258445b57f39bf29a966c6c..057e6944755161b5fa9b9be36374ee567e82f178 100644
--- a/README.md
+++ b/README.md
@@ -52,9 +52,9 @@ Buttons: 4, named as the routes above.
 
 ### Back-end
 
-:construction: Session management subsystem written in Python/FlaskGunicorn/???
+:construction: Session management subsystem written in Python/Flask
 
-:hammer_and_wrench: This part requires some development by the students.
+:tools: This part requires some development by the students.
 
 
 ### Storage
@@ -62,22 +62,110 @@ Buttons: 4, named as the routes above.
 :construction: S3-like object storage composed of 1 buckets with two directories: one for
 *enrollment* data, one for *session* data.
 
+Objects shall be written as JSON data based on the following proposed schema.
+
+
+#### Enrollment objects
+
+An enrollment object:
+  * is named after the user e-mail
+  * is unique in the system
+  * is created when a new user subscribes to the system via the `enroll` function
+  * is never updated (assume that users' passwords cannot be changed)
+  * is deleted when a subscribed (== existing) user unsubscribes from the
+    system via the `unenroll` function
+
+Minimum schema (you're free to extend it):
+
+``` json
+{
+	"title": "Enrollment",
+	"type": "object",
+	"properties": {
+		"password": {
+			"type": "string",
+			"description": "The user's password."
+		},
+		"timestamp": {
+			"description": "The UNIX epoch time of object's creation",
+			"type": "integer",
+			"minimum": 0
+		}
+	}
+}
+```
+
+Example data for object named `foo@bar.com`:
+``` json
+{
+	"password": "SECRET",
+	"timestamp": 1733330967
+}
+```
+
+
+#### Session objects
+
+A session object:
+  * is named after the user e-mail
+  * is unique in the system -- multiple sessions are not permitted
+  * is created at the first user's connection to the system via the `login`
+    function
+  * is never updated
+  * is deleted when a logged-in user disconnects from the system via the
+    `logout` function
+
+Minimum schema (you're free to extend it):
+
+``` json
+{
+	"title": "Session",
+	"type": "object",
+	"properties": {
+		"client": {
+			"description": "The client host's IPv4 address",
+			"type": "string",
+			"format": "ipv4"
+		},
+		"timestamp": {
+			"description": "The UNIX epoch time of object's creation",
+			"type": "integer",
+			"minimum": 0
+		}
+	}
+}
+```
+
+Example data for object named `foo@bar.com`:
+``` json
+{
+	"timestamp": 1733330967
+}
+```
+
 
 ### Workflows
 
-The following workflows correspond to the front-end routes discussed above.
+The following workflows correspond to the application "routes" discussed
+above. Additional information is provided in the boilerplate files. All
+involved data are in JSON format.
 
 Conventions:
   * KEYWORDS are capitalized.
   * **Actors** are in boldface.
   * 'CONSTANTS' are capitalized and single-quoted.
   * *data* items are lower-case and italicized.
+  * all times are represented as [UNIX epoch
+    timestamps](https://en.wikipedia.org/wiki/Unix_time)
 
-:bulb: This is a fake SSO ;-) For the sake of simplicity:
+:bulb: For the sake of simplicity:
   * Sessions never expire.
   * No cookies are stored at the client.
   * The e-mail address is used as authentication token.
 
+All operations are *stateless*: apart from enrollment and session data,
+nothing else is recorded by the back-end.
+
 
 #### Enroll
 
@@ -90,7 +178,7 @@ A new user subscribes to the system via the `enroll` function:
       - IF user exists THEN returns 'KO:ALREADY_ENROLLED'
       - ELSE
           1. Writes enrollment data to storage
-          2. Returns 'OK' to the front-end
+          2. Returns 'OK:ENROLLED' to the front-end
   4. **Front-end** receives response from the back-end and shows it to the user.
 
 
@@ -102,10 +190,10 @@ An enrolled unsubscribes from the system with the `unenroll` function:
      front-end.
   2. **Front-end** sends enrollment data to the back-end.
   3. **Back-end** verifies enrollment data:
-      - IF user does not exists THEN returns 'KO:NO_SUCH_USER'
+      - IF user does not exists THEN returns 'KO:NO\_SUCH\_USER'
       - ELSE
           1. Removes enrollment data and any active sessions from the storage
-          2. Returns 'OK' to the front-end
+          2. Returns 'OK:UNENROLLED' to the front-end
   4. **Front-end** receives response from the back-end and shows it to the user.
 
 
@@ -116,22 +204,22 @@ An enrolled user authenticates to the system with the `login` function:
   1. **User** provides *e-mail* via the the front-end.
   2. **Front-end** sends *e-mail* to the back-end.
   3. **Back-end** verifies the *e-mail*:
-      - IF user does not exists THEN returns 'KO:NO_SUCH_USER'
+      - IF user does not exists THEN returns 'KO:NO\_SUCH\_USER'
       - ELSE verifies sessions:
-          1. IF an active session exists THEN returns 'OK'
-          2. ELSE returns 'NEED_PASSWORD'.
+          1. IF an active session exists THEN returns 'OK:SESSION_EXISTS'
+          2. ELSE returns 'OK:NEED_PASSWORD'.
   4. **Front-end** receives first response from the back-end:
-      - IF response == 'NEED_PASSWORD' THEN
+      - IF response == 'OK:NEED_PASSWORD' THEN
           1. Prompts the user for their *password*.
           2. **User** provides *password*.
           3. **Front-end** sends *e-mail* and *password* to the back-end.
           4. **Back-end** verifies the *password*:
               - IF *password* matches THEN
-                  1. Writes session to storage.
-                  2. Returns 'OK' to the front-end.
+                  1. Writes a new session to storage.
+                  2. Returns 'OK:LOGGED_IN' to the front-end.
               - ELSE returns 'KO:WRONG_PASSWORD' to the front-end.
-      - ELSE shows 'OK' response to the user and terminates.
-  5. **Front-end** receives second response 'OK/KO' from the back-end and
+      - ELSE shows other response to the user and terminates.
+  5. **Front-end** receives second response from the back-end and
      shows it to the user.
 
 
@@ -142,12 +230,12 @@ An enrolled user deauthenticates to the system with the `logout` function:
   1. **User** provides *e-mail* via the the front-end.
   2. **Front-end** sends *e-mail* to the back-end.
   3. **Back-end** verifies the *e-mail*:
-      - IF user does not exists THEN returns 'KO'
+      - IF user does not exists THEN returns 'KO:NO\_SUCH\_USER'
       - ELSE verifies sessions:
           - IF an active session exists THEN
               1. Removes session from storage.
-              2. Returns 'OK'.
-          - ELSE returns 'KO'.
+              2. Returns 'OK:LOGGED_OUT'.
+          - ELSE returns 'KO:NO\_ACTIVE\_SESSION'.
   4. **Front-end** receives response from the back-end and shows it to the user.
 
 
@@ -186,18 +274,53 @@ whenever its image is updated.
 
 ## Tasks
 
+:construction: **To be finalized**
+
 You shall:
 
+0. Fork this repository.
 1. Complete the Python back-end file(s) in folder `Application/back-end.py`.
-2. Rebuild the application Docker image, and store it :question: somewhere --
-   **(TO-DO: We should provide instructions. Build on the student's
-   workstation?)**.
-3. Complete the `Terraform/main.tf` recipe to handle the provisioning of the S3 storage
-   bucket.
-4. Complete the `Ansible/deploy.yml` playbook to handle:
-    - exposure of the application portal IP (e.g., load-balancer IP) to the
+2. Rebuild the application Docker image, and store it (somewhere) --
+   **(:question: TO-DO - We should provide instructions + Dockerfile:
+    - Build image on the student's workstation
+    - What's better: push to Dockerhub vs to scp to VM + import?
+    )**. This task shall be automated via Ansible -- see below.
+3. Complete your Terraform files from the version you developed in
+   [Lab-Terraform](https://gitedu.hesge.ch/lsds/teaching/bachelor/cloud-and-deployment/lab-terraform/-/blob/main/SwitchEngines/README.md)
+   up to Task #8. Your recipe shall handle only the provisioning of the VM
+   plus an S3 storage bucket -- no KinD/Kubectl package installation. Commit
+   your recipe files (included Cloud-init) and  in directory `Terraform/`.
+4. Complete your Ansible playbook, starting from the version you developed in
+   [Lab-Ansible](https://gitedu.hesge.ch/lsds/teaching/bachelor/cloud-and-deployment/lab-ansible)
+   Task #10, to:
+    - expose the application portal IP (e.g., load-balancer IP) to the
       Internet via `socat` or other mechanism of your choice;
-    - :question: **(TO-DO: What's better? Local or registry [Helm/Docker])?**
-      transfer/download of the application image to your VM instance.
-
-Tests: :construction: **TO-DO**
+    - :question: **(TO-DO: What's better? Local or registry [Docker])?**
+      rebuild and transfer/download the application image to your VM instance.
+   Commit these files in directory `Ansible/`.
+
+
+### Tests
+
+The following tests shall be passed by your implementation:
+
+  * Starting with a non-provisioned infrastructure:
+      1. Terraform apply shall provision a bare-bone VM and an S3 bucket
+      2. Ansible-playbook shall install KinD/Kubectl and deploy your
+         load-balanced application
+      3. Your application shall be reachable on port 80 (or another of your
+         choice) from any host outside the Cloud network.
+  * Once your application is installed, you shall exercise all the branch
+    conditions described by the above workflows:
+      1. Enroll a new user: shall succeed
+      2. Enroll an existing: user shall fail
+      3. Unenroll a new user: shall fail
+      4. Unenroll an existing user: shall succeed
+      5. Login an enrolled user: shall succeed and ask for the password
+          * with a valid password: shall succeed
+          * with an invalid password: shall fail
+      6. Login a non-enrolled user: shall fail
+      7. Logout a non-enrolled user: shall fail
+      8. Logout an enrolled user
+          * with an active session: shall succeed
+          * without an active session: shall fail