diff --git a/src/InSillyCloWeb/assemblies/models.py b/src/InSillyCloWeb/assemblies/models.py
index 4402f6119d4a8cfee6a4f3b7a45026d1c29304a3..8f4dfa70bffc27adbbf1258e6eeb4d880f557fb7 100644
--- a/src/InSillyCloWeb/assemblies/models.py
+++ b/src/InSillyCloWeb/assemblies/models.py
@@ -19,7 +19,6 @@ from django.db.models.functions import Upper
 from django.urls import reverse
 from django.utils.translation import gettext_lazy as _
 from django.conf import settings
-from django.contrib.auth.signals import user_logged_in
 
 from . import utils
 from .insillyclo_impl import InSillyCloDjangoMessageObserver
@@ -251,13 +250,10 @@ class Assembly(models.Model):
 
 
 class JobStatus(models.IntegerChoices):
-    NEW = 0, _("New")
-    QUEUED = 1, _("Queued")
-    FETCHING = 5, _("Fetching")
-    RUNNING = 10, _("Running")
-    DONE = 15, _("Done")
+    NEW = 0, _("CREATED")
+    DONE = 15, _("DONE")
     ERROR = 16, _("Error")
-    CANCELED = 17, _("Canceled")
+    CANCELED = 17, _("CANCELED")
 
     __empty__ = "(Unknown)"
 
@@ -438,8 +434,12 @@ class SimulatorJob(models.Model):
                 enzyme_names=self.enzyme_names,
             )
             messages.info(request, f"TODO: make use of {str(output)}")
+            self.status = JobStatus.DONE
+            self.save()
         except insillyclo.additional_exception.InSillyCloFailureException as e:
             output = None
+            self.status = JobStatus.ERROR
+            self.save()
             messages.error(request, utils.clear_sensitive_info(str(e), self))
         return output
 
@@ -471,6 +471,10 @@ class SimulatorJob(models.Model):
     def results_dir(self) -> pathlib.Path:
         return self.job_dir / 'results'
 
+    @property
+    def is_downloadable(self):
+        return self.status == JobStatus.DONE and self.results_dir.exists()
+
     def delete(self, *args, **kwargs):
         try:
             shutil.rmtree(self.job_dir)
@@ -481,12 +485,14 @@ class SimulatorJob(models.Model):
     def get_absolute_url(self):
         return reverse("assemblies:simulator-detail", args=[self.uuid])
 
-    def __str__(self):
-        return f'{str(self.uuid)[:8]} - {str(self.created_at)}'
-
-
-def assign_object_to_user(sender, user, request, **kwargs):
-    print(request.session.session_key)
+    def results_file(self):
+        basename = f"insillyclo_{self.uuid_short}"
+        zipname = os.path.join(self.job_dir, basename)
+        destination_file = pathlib.Path(zipname + '.zip')
+        shutil.make_archive(zipname, 'zip', self.results_dir)
 
+        with open(destination_file, "rb") as fh:
+            return BytesIO(fh.read()), basename, 'application/zip'
 
-user_logged_in.connect(assign_object_to_user)
+    def __str__(self):
+        return f'{str(self.uuid)[:8]} - {str(self.created_at)}'
diff --git a/src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_card.html b/src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_card.html
index 3a109201bc4d28e8ecd4507e64a800e0518d0086..e89cc32b9494fe5a88a2d4bc54a1498e8a51cbec 100644
--- a/src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_card.html
+++ b/src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_card.html
@@ -1,18 +1,21 @@
 {% load assemblies_tags %}
 {% load i18n %}
 
-<div class="card context-simulator">
+<div class="card border-simulator">
     <div class="card-body row">
-        <a class="col-12 col-xl-2 col-lg-3 col-md-3 me-md-4 text-decoration-none"  href="{% url 'assemblies:simulator-detail' uuid=object.uuid %}">
+        <a class="col-12 col-xl-2 col-lg-3 col-md-3 me-md-4 text-simulator text-decoration-none"  href="{% url 'assemblies:simulator-detail' uuid=object.uuid %}">
             <div class="fw-bolder">
                 Job #{{object.uuid_short}}
             </div>
+            <small class="text-secondary">
+                {{ object.get_status_display }}
+            </small>
             <small class="text-secondary">
                 {{object|field_verbose_name:'updated_at'}}{%trans ':'%}<br/>
                 {{object.updated_at}}
             </small>
         </a>
-        <div class="col border-10 border-start border-primary ps-md-4">
+        <div class="col border-10 border-start border-simulator text-simulator ps-md-4">
              
             <div class="d-block">
                 <span class="fw-bolder">
@@ -42,12 +45,14 @@
             </div>
         </div>
 
+        {% if object.is_downloadable %}
         <div class="col-auto">
-            <a href=""
+            <a href="{% url 'assemblies:simulator-download' uuid=object.uuid %}"
             class="btn btn-designer btn-md">
                 <i class="bi bi-cloud-arrow-down fs-3"></i>
             </a>
         </div>
+        {% endif %}
         
     </div>
 </div>
diff --git a/src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_detail.html b/src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_detail.html
index a4203fc3e76aaade3f22fadfb87beb8ed96054c1..e9760345438369cbb5b9e7b97bf107a958c21fe8 100644
--- a/src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_detail.html
+++ b/src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_detail.html
@@ -30,7 +30,7 @@
 {% block page_sub_title %}Assembly simulator{% endblock %}
 
 {%block content %}
-<h6 class="col-12 text-center">
+<h6 class="col-12 text-center text-simulator">
     Run on {{object.updated_at}}
 </h6>
 <h1 class="col-12 text-center">
diff --git a/src/InSillyCloWeb/assemblies/urls.py b/src/InSillyCloWeb/assemblies/urls.py
index ba9d99e20ff0e9105405ae1981495ffbc46c2b89..f73cd661e13aa92cca8d0f53c37484bda4be0bd6 100644
--- a/src/InSillyCloWeb/assemblies/urls.py
+++ b/src/InSillyCloWeb/assemblies/urls.py
@@ -69,6 +69,7 @@ urlpatterns = [
     path('assembly-simulator/create/<str:step>/', simulator_wizard, name='simulator-create-step'),
     path('assembly-simulator/create/', simulator_wizard, name='simulator-create'),
     path('assembly-simulator/<slug:uuid>/', views.JobSimulatorResult.as_view(), name='simulator-detail'),
+    path('assembly-simulator/<slug:uuid>/download/', views.JobDownloadView.as_view(), name='simulator-download'),
     path('assembly-simulator/<slug:uuid>/pcr-edit/', views.JobPCREdit.as_view(), name='simulator-pcr-edit'),
     path(
         'assembly-simulator/<slug:uuid>/enzyme-edit/',
diff --git a/src/InSillyCloWeb/assemblies/views.py b/src/InSillyCloWeb/assemblies/views.py
index ba59624e1d0d3287ba0ead133598b3bc1d6ee5a5..5688aaedf505e7dfb99715f7eeadca79206dd9bb 100644
--- a/src/InSillyCloWeb/assemblies/views.py
+++ b/src/InSillyCloWeb/assemblies/views.py
@@ -192,6 +192,29 @@ class JobRestrictionEnzymeEdit(
         return _("Restriction enzyme to use for gel simulation")
 
 
+class JobDownloadView(
+    SingleObjectMixin,
+    View,
+):
+    model = models.SimulatorJob
+    slug_field = 'uuid'
+    slug_url_kwarg = 'uuid'
+
+    def get_file(self) -> Tuple[BytesIO, str, str]:
+        return self.get_object().results_file()
+
+    def get(self, request, *args, **kwargs):
+        try:
+            stream, filename, content_type = self.get_file()
+            response = HttpResponse(stream.read(), content_type=content_type)
+            response['Content-Disposition'] = f'attachment; filename={filename}'
+            return response
+        except Exception as e:
+            traceback.print_exc()
+            messages.error(request, str(e.__class__) + str(e))
+            return redirect(request.headers['Referer'])
+
+
 def loginView(request):
 
     if request.user.is_authenticated: