default_app_config = 'mathesar.apps.MathesarConfig'

__version__ = "0.1.2"

mathesar/__init__.py
default_app_config = 'mathesar.apps.MathesarConfig'

__version__ = "0.1.3"

mathesar/__init__.py
diff --git a/db/sql/0_msar.sql b/db/sql/0_msar.sql
index 9f9a41037d..48f49f876b 100644
--- a/db/sql/0_msar.sql
+++ b/db/sql/0_msar.sql
@@ -243,7 +243,7 @@ Args:
 */
 SELECT array_agg(
   CASE
-    WHEN rel_id=0 THEN quote_ident(col::text)
+    WHEN rel_id=0 THEN quote_ident(col #>> '{}')
     WHEN jsonb_typeof(col)='number' THEN msar.get_column_name(rel_id, col::integer)
     WHEN jsonb_typeof(col)='string' THEN msar.get_column_name(rel_id, col #>> '{}')
   END
@@ -1016,8 +1016,8 @@ Get a JSON array of column definitions from given columns for creation of an ext
 See the msar.process_col_def_jsonb for a description of the JSON.
 
 Args:
-  tab_id: The OID of the table containing the column whose definition we want.
-  col_ids: The attnum of the column whose definitions we want.
+  tab_id: The OID of the table containing the columns whose definitions we want.
+  col_ids: The attnum of the columns whose definitions we want.
 */
 
 SELECT jsonb_agg(
@@ -1697,6 +1697,37 @@ END;
 $$ LANGUAGE plpgsql;
 
 
+CREATE OR REPLACE FUNCTION
+msar.get_extracted_con_def_jsonb(tab_id oid, col_ids integer[]) RETURNS jsonb AS $$/*
+Get a JSON array of constraint definitions from given columns for creation of an extracted table.
+
+See the msar.process_con_def_jsonb for a description of the JSON.
+
+Args:
+  tab_id: The OID of the table containing the constraints whose definitions we want.
+  col_ids: The attnum of columns with the constraints whose definitions we want.
+*/
+
+SELECT jsonb_agg(
+  jsonb_build_object(
+    'type', contype,
+    'columns', ARRAY[attname],
+    'deferrable', condeferrable,
+    'fkey_relation_id', confrelid::integer,
+    'fkey_columns', confkey,
+    'fkey_update_action', confupdtype,
+    'fkey_delete_action', confdeltype,
+    'fkey_match_type', confmatchtype
+  )
+)
+FROM pg_constraint
+  JOIN unnest(col_ids) AS columns_to_copy(col_id) ON pg_constraint.conkey[1]=columns_to_copy.col_id
+  JOIN pg_attribute
+    ON pg_attribute.attnum=columns_to_copy.col_id AND pg_attribute.attrelid=pg_constraint.conrelid
+WHERE pg_constraint.conrelid=tab_id AND (pg_constraint.contype='f' OR pg_constraint.contype='u');
+$$ LANGUAGE sql RETURNS NULL ON NULL INPUT;
+
+
 ----------------------------------------------------------------------------------------------------
 ----------------------------------------------------------------------------------------------------
 -- MATHESAR DROP TABLE FUNCTIONS
@@ -1849,7 +1880,11 @@ WITH col_cte AS (
   SELECT string_agg(__msar.build_con_def_text(con), ', ') AS table_constraints
   FROM unnest(con_defs) as con
 )
-SELECT __msar.exec_ddl('CREATE TABLE %s (%s) %s', tab_name, table_columns, table_constraints)
+SELECT __msar.exec_ddl(
+  'CREATE TABLE %s (%s)',
+  tab_name,
+  concat_ws(', ', table_columns, table_constraints)
+)
 FROM col_cte, con_cte;
 $$ LANGUAGE SQL;
 
@@ -1878,8 +1913,8 @@ DECLARE
   constraint_defs __msar.con_def[];
 BEGIN
   fq_table_name := format('%s.%s', __msar.get_schema_name(sch_oid), quote_ident(tab_name));
-  column_defs := msar.process_col_def_jsonb(null, col_defs, false, true);
-  constraint_defs := msar.process_con_def_jsonb(null, con_defs);
+  column_defs := msar.process_col_def_jsonb(0, col_defs, false, true);
+  constraint_defs := msar.process_con_def_jsonb(0, con_defs);
   PERFORM __msar.add_table(fq_table_name, column_defs, constraint_defs);
   created_table_id := fq_table_name::regclass::oid;
   PERFORM msar.comment_on_table(created_table_id, comment_);
@@ -2324,6 +2359,7 @@ The extraction takes a set of columns from the table, and creates a new table fr
 */
 DECLARE
   extracted_col_defs CONSTANT jsonb := msar.get_extracted_col_def_jsonb(tab_id, col_ids);
+  extracted_con_defs CONSTANT jsonb := msar.get_extracted_con_def_jsonb(tab_id, col_ids);
   fkey_name CONSTANT text := msar.build_unique_fkey_column_name(tab_id, fk_col_name, new_tab_name);
   extracted_table_id integer;
   fkey_attnum integer;
@@ -2333,7 +2369,7 @@ BEGIN
     msar.get_relation_namespace_oid(tab_id),
     new_tab_name,
     extracted_col_defs,
-    null,
+    extracted_con_defs,
     format('Extracted from %s', __msar.get_relation_name(tab_id))
   );
   -- Create a new fkey column and foreign key linking the original table to the extracted one.
diff --git a/db/sql/test_0_msar.sql b/db/sql/test_0_msar.sql
index 23b08c302f..52adbd51fa 100644
--- a/db/sql/test_0_msar.sql
+++ b/db/sql/test_0_msar.sql
@@ -1871,7 +1871,7 @@ END;
 $$ LANGUAGE plpgsql;
 
 
-CREATE OR REPLACE FUNCTION test_extract_columns() RETURNS SETOF TEXT AS $f$
+CREATE OR REPLACE FUNCTION test_extract_columns_data() RETURNS SETOF TEXT AS $f$
 BEGIN
   CREATE TABLE roster_snapshot AS SELECT * FROM "Roster" ORDER BY id;
   PERFORM msar.extract_columns_from_table('"Roster"'::regclass::oid, ARRAY[3, 4], 'Teachers', null);
@@ -1906,6 +1906,38 @@ END;
 $f$ LANGUAGE plpgsql;
 
 
+CREATE OR REPLACE FUNCTION setup_extract_fkey_cols() RETURNS SETOF TEXT AS $$
+BEGIN
+CREATE TABLE "Referent" (
+    id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
+    "Teacher" text,
+    "Teacher Email" text
+);
+CREATE TABLE "Referrer" (
+    id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
+    "Student Name" text,
+    "Subject" varchar(20),
+    "Grade" integer,
+    "Referent_id" integer REFERENCES "Referent" (id)
+);
+END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE OR REPLACE FUNCTION test_extract_columns_keeps_fkey() RETURNS SETOF TEXT AS $f$
+BEGIN
+  PERFORM msar.extract_columns_from_table(
+    '"Referrer"'::regclass::oid, ARRAY[3, 5], 'Classes', 'Class'
+  );
+  RETURN NEXT columns_are('Referent', ARRAY['id', 'Teacher', 'Teacher Email']);
+  RETURN NEXT columns_are('Referrer', ARRAY['id', 'Student Name', 'Grade', 'Class']);
+  RETURN NEXT columns_are('Classes', ARRAY['id', 'Subject', 'Referent_id']);
+  RETURN NEXT fk_ok('Referrer', 'Class', 'Classes', 'id');
+  RETURN NEXT fk_ok('Classes', 'Referent_id', 'Referent', 'id');
+END;
+$f$ LANGUAGE plpgsql;
+
+
 CREATE OR REPLACE FUNCTION setup_dynamic_defaults() RETURNS SETOF TEXT AS $$
 BEGIN
   CREATE TABLE defaults_test (
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index a62125381d..55bc284f85 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -4,7 +4,7 @@ repo_url: https://github.com/centerofci/mathesar/
 repo_name: centerofci/mathesar
 
 nav:
-  - Introduction: 
+  - Introduction:
       - Welcome: index.md
   - Installation:
       - Install with Docker Compose: installation/docker-compose/index.md
@@ -32,22 +32,22 @@ plugins:
       lang: en
   - redirects:
       redirect_maps:
-          'installation-dc/administration.md': 'installation/guided-install/index.md#start-stop'
-          'installation-dc/ansible-setup.md': 'installation/docker-compose/index.md'
-          'installation-dc/quickstart.md': 'installation/guided-install/index.md'
-          'installation-dc/troubleshooting.md': 'installation/guided-install/troubleshooting.md'
-          'installation-dc/under-the-hood.md': 'installation/guided-install/under-the-hood.md'
-          'installation-dc/uninstall.md': 'installation/guided-install/index.md#uninstall'
-          'product/intro.md': 'user-guide/index.md'
-          'product/syncing-db.md': 'user-guide/syncing-db.md'
-          'product/users.md': 'user-guide/users.md'
-          'install/index.md': 'index.md'
-          'install/guided-install/index.md': 'installation/guided-install/index.md'
-          'install/docker-compose/index.md': 'installation/docker-compose/index.md'
-          'install/docker-compose/under-the-hood.md': 'installation/guided-install/under-the-hood.md'
-          'install/docker-compose/troubleshooting.md': 'installation/guided-install/troubleshooting.md'
-          'install/docker/index.md': 'installation/docker/index.md'
-          'install/build-from-source/index.md': 'installation/build-from-source/index.md'
+        "installation-dc/administration.md": "installation/guided-install/index.md#start-stop"
+        "installation-dc/ansible-setup.md": "installation/docker-compose/index.md"
+        "installation-dc/quickstart.md": "installation/guided-install/index.md"
+        "installation-dc/troubleshooting.md": "installation/guided-install/troubleshooting.md"
+        "installation-dc/under-the-hood.md": "installation/guided-install/under-the-hood.md"
+        "installation-dc/uninstall.md": "installation/guided-install/index.md#uninstall"
+        "product/intro.md": "user-guide/index.md"
+        "product/syncing-db.md": "user-guide/syncing-db.md"
+        "product/users.md": "user-guide/users.md"
+        "install/index.md": "index.md"
+        "install/guided-install/index.md": "installation/guided-install/index.md"
+        "install/docker-compose/index.md": "installation/docker-compose/index.md"
+        "install/docker-compose/under-the-hood.md": "installation/guided-install/under-the-hood.md"
+        "install/docker-compose/troubleshooting.md": "installation/guided-install/troubleshooting.md"
+        "install/docker/index.md": "installation/docker/index.md"
+        "install/build-from-source/index.md": "installation/build-from-source/index.md"
   - macros
   - placeholder
 
@@ -56,19 +56,19 @@ theme:
   logo: assets/images/logo.svg
   favicon: assets/images/favicon.ico
   features:
-      - content.code.copy
-      - navigation.sections
-      - navigation.expand
+    - content.code.copy
+    - navigation.sections
+    - navigation.expand
   font:
     text: Nunito Sans
     code: Fira Code
   icon:
     repo: fontawesome/brands/github
-  palette: 
+  palette:
     # Palette toggle for light mode
     - scheme: default
       toggle:
-        icon: material/brightness-7 
+        icon: material/brightness-7
         name: Switch to dark mode
     # Palette toggle for dark mode
     - scheme: slate
@@ -101,4 +101,4 @@ markdown_extensions:
       permalink: true
 
 extra:
-  mathesar_version: 0.1.2
+  mathesar_version: 0.1.3
diff --git a/install.sh b/install.sh
index 188e6139d7..1fda25f5b9 100755
--- a/install.sh
+++ b/install.sh
@@ -1,7 +1,7 @@
 #!/usr/bin/env bash
 set -e
 clear -x
-github_tag=${1-"0.1.2"}
+github_tag=${1-"0.1.3"}
 min_maj_docker_version=20
 min_maj_docker_compose_version=2
 min_min_docker_compose_version=7
diff --git a/mathesar/__init__.py b/mathesar/__init__.py
index 278d0e3e53..eb27ba2967 100644
--- a/mathesar/__init__.py
+++ b/mathesar/__init__.py
@@ -1,3 +1,3 @@
 default_app_config = 'mathesar.apps.MathesarConfig'
 
-__version__ = "0.1.2"
+__version__ = "0.1.3"
diff --git a/mathesar_ui/src/App.svelte b/mathesar_ui/src/App.svelte
index 1283f3167a..4fe08c214e 100644
--- a/mathesar_ui/src/App.svelte
+++ b/mathesar_ui/src/App.svelte
@@ -1,87 +1,45 @@
 <script lang="ts">
-  import {
-    Confirmation,
-    Spinner,
-    ToastPresenter,
-  } from '@mathesar-component-library';
-  import { confirmationController } from '@mathesar/stores/confirmation';
-  import { toast } from '@mathesar/stores/toast';
-  import {
-    RecordSelectorController,
-    setRecordSelectorControllerInContext,
-  } from '@mathesar/systems/record-selector/RecordSelectorController';
+  import { Spinner } from '@mathesar-component-library';
   import { preloadCommonData } from '@mathesar/utils/preloadData';
+  import AppContext from './AppContext.svelte';
   import RootRoute from './routes/RootRoute.svelte';
-  import { setNewClipboardHandlerStoreInContext } from './stores/clipboard';
-  import { modal } from './stores/modal';
-  import ModalRecordSelector from './systems/record-selector/ModalRecordSelector.svelte';
   import { loadLocaleAsync } from './i18n/i18n-load';
   import { setLocale } from './i18n/i18n-svelte';
+  import type { RequestStatus } from './api/utils/requestUtils';
+  import { getErrorMessage } from './utils/errors';
+  import ErrorBox from './components/message-boxes/ErrorBox.svelte';
 
   /**
    * Later the translations file will be loaded
    * in parallel to the FE's first chunk
    */
-  let isTranslationsLoaded = false;
+  let translationLoadStatus: RequestStatus = { state: 'processing' };
   void (async () => {
-    await loadLocaleAsync('en');
-    setLocale('en');
-    isTranslationsLoaded = true;
+    try {
+      await loadLocaleAsync('en');
+      setLocale('en');
+      translationLoadStatus = { state: 'success' };
+    } catch (exp) {
+      translationLoadStatus = {
+        state: 'failure',
+        errors: [getErrorMessage(exp)],
+      };
+    }
   })();
 
   const commonData = preloadCommonData();
-
-  const clipboardHandlerStore = setNewClipboardHandlerStoreInContext();
-  const recordSelectorModal = modal.spawnModalController();
-  const recordSelectorController = new RecordSelectorController({
-    onOpen: () => recordSelectorModal.open(),
-    onClose: () => recordSelectorModal.close(),
-    nestingLevel: 0,
-  });
-  setRecordSelectorControllerInContext(recordSelectorController);
-
-  $: clipboardHandler = $clipboardHandlerStore;
-
-  // Why are we handling clipboard events here?
-  //
-  // We originally implemented the clipboard handler lower down, in the Sheet
-  // component. That worked for Firefox because when the user pressed Ctrl+C the
-  // focused `.cell-wrapper` div node would emit a copy event. However, in
-  // Chrome and Safari, the focused `.cell-wrapper` div node does _not_ emit
-  // copy events! Perhaps that's because it doesn't contain any selected text?
-  // Instead, the copy event gets emitted from `body` in Chrome/Safari.
-  // Clipboard functionality seems inconsistent in subtle ways across browsers.
-  // Make sure to test in all browsers when making changes!
-  //
-  // On a record page with multiple table widgets, we should be able to copy
-  // cells from each table widget, and we should be able to copy plain text on
-  // the page, outside of the sheet. We also need to support copying from the
-  // Data Explorer.
-
-  function handleCopy(e: ClipboardEvent) {
-    if (clipboardHandler) {
-      clipboardHandler.handleCopy(e);
-      e.preventDefault();
-    }
-  }
 </script>
 
-<svelte:body on:copy={handleCopy} />
-
-{#if isTranslationsLoaded}
-  <ToastPresenter entries={toast.entries} />
-  <Confirmation controller={confirmationController} />
-  <ModalRecordSelector
-    {recordSelectorController}
-    modalController={recordSelectorModal}
-  />
-  {#if commonData}
+{#if translationLoadStatus.state === 'success' && commonData}
+  <AppContext {commonData}>
     <RootRoute {commonData} />
-  {/if}
-{:else}
+  </AppContext>
+{:else if translationLoadStatus.state === 'processing'}
   <div class="app-loader">
     <Spinner size="2rem" />
   </div>
+{:else}
+  <ErrorBox>This state should never occur.</ErrorBox>
 {/if}
 
 <!--
diff --git a/mathesar_ui/src/AppContext.svelte b/mathesar_ui/src/AppContext.svelte
new file mode 100644
index 0000000000..e153ef508d
--- /dev/null
+++ b/mathesar_ui/src/AppContext.svelte
@@ -0,0 +1,84 @@
+<script lang="ts">
+  import { get } from 'svelte/store';
+  import { Confirmation, ToastPresenter } from '@mathesar-component-library';
+  import type { CommonData } from '@mathesar/utils/preloadData';
+  import { confirmationController } from '@mathesar/stores/confirmation';
+  import { toast } from '@mathesar/stores/toast';
+  import { setNewClipboardHandlerStoreInContext } from '@mathesar/stores/clipboard';
+  import { setUserProfileStoreInContext } from '@mathesar/stores/userProfile';
+  import { setReleasesStoreInContext } from '@mathesar/stores/releases';
+  import { AnonymousViewerUserModel } from '@mathesar/stores/users';
+  import { modal } from '@mathesar/stores/modal';
+  import {
+    RecordSelectorController,
+    setRecordSelectorControllerInContext,
+  } from '@mathesar/systems/record-selector/RecordSelectorController';
+  import ModalRecordSelector from '@mathesar/systems/record-selector/ModalRecordSelector.svelte';
+  import { setBreadcrumbItemsInContext } from '@mathesar/components/breadcrumb/breadcrumbUtils';
+
+  export let commonData: CommonData;
+
+  setBreadcrumbItemsInContext([]);
+
+  function setUserProfileAndReleaseStores() {
+    const user =
+      commonData.routing_context === 'anonymous'
+        ? new AnonymousViewerUserModel()
+        : commonData.user;
+
+    const userProfileStore = setUserProfileStoreInContext(user);
+    const userProfile = get(userProfileStore);
+    if (commonData.is_authenticated && userProfile.isSuperUser) {
+      // Toggle these lines to test with a mock tag name
+      // setReleasesStoreInContext('1.75.0');
+      setReleasesStoreInContext(commonData.current_release_tag_name);
+    }
+  }
+
+  $: commonData, setUserProfileAndReleaseStores();
+
+  const recordSelectorModal = modal.spawnModalController();
+  const recordSelectorController = new RecordSelectorController({
+    onOpen: () => recordSelectorModal.open(),
+    onClose: () => recordSelectorModal.close(),
+    nestingLevel: 0,
+  });
+  setRecordSelectorControllerInContext(recordSelectorController);
+
+  const clipboardHandlerStore = setNewClipboardHandlerStoreInContext();
+  $: clipboardHandler = $clipboardHandlerStore;
+
+  // Why are we handling clipboard events here?
+  //
+  // We originally implemented the clipboard handler lower down, in the Sheet
+  // component. That worked for Firefox because when the user pressed Ctrl+C the
+  // focused `.cell-wrapper` div node would emit a copy event. However, in
+  // Chrome and Safari, the focused `.cell-wrapper` div node does _not_ emit
+  // copy events! Perhaps that's because it doesn't contain any selected text?
+  // Instead, the copy event gets emitted from `body` in Chrome/Safari.
+  // Clipboard functionality seems inconsistent in subtle ways across browsers.
+  // Make sure to test in all browsers when making changes!
+  //
+  // On a record page with multiple table widgets, we should be able to copy
+  // cells from each table widget, and we should be able to copy plain text on
+  // the page, outside of the sheet. We also need to support copying from the
+  // Data Explorer.
+
+  function handleCopy(e: ClipboardEvent) {
+    if (clipboardHandler) {
+      clipboardHandler.handleCopy(e);
+      e.preventDefault();
+    }
+  }
+</script>
+
+<svelte:body on:copy={handleCopy} />
+
+<ToastPresenter entries={toast.entries} />
+<Confirmation controller={confirmationController} />
+<ModalRecordSelector
+  {recordSelectorController}
+  modalController={recordSelectorModal}
+/>
+
+<slot />
diff --git a/mathesar_ui/src/routes/AnonymousAccessRoutes.svelte b/mathesar_ui/src/routes/AnonymousAccessRoutes.svelte
index 468069496e..a62230e9f8 100644
--- a/mathesar_ui/src/routes/AnonymousAccessRoutes.svelte
+++ b/mathesar_ui/src/routes/AnonymousAccessRoutes.svelte
@@ -1,11 +1,7 @@
 <script lang="ts">
   import { Route } from 'tinro';
-  import { setUserProfileStoreInContext } from '@mathesar/stores/userProfile';
-  import { AnonymousViewerUserModel } from '@mathesar/stores/users';
   import SharedTableRoute from '@mathesar/routes/SharedTableRoute.svelte';
   import SharedExplorationRoute from '@mathesar/routes/SharedExplorationRoute.svelte';
-
-  setUserProfileStoreInContext(new AnonymousViewerUserModel());
 </script>
 
 <Route path="/tables/:slug/*" let:meta>
diff --git a/mathesar_ui/src/routes/AuthenticatedRoutes.svelte b/mathesar_ui/src/routes/AuthenticatedRoutes.svelte
index 95bb6c5dc4..a792229898 100644
--- a/mathesar_ui/src/routes/AuthenticatedRoutes.svelte
+++ b/mathesar_ui/src/routes/AuthenticatedRoutes.svelte
@@ -1,24 +1,15 @@
 <script lang="ts">
   import { Route } from 'tinro';
-  import type { CommonData } from '@mathesar/utils/preloadData';
   import ErrorPage from '@mathesar/pages/ErrorPage.svelte';
   import { databases } from '@mathesar/stores/databases';
-  import { setReleasesStoreInContext } from '@mathesar/stores/releases';
-  import { setUserProfileStoreInContext } from '@mathesar/stores/userProfile';
+  import { getUserProfileStoreFromContext } from '@mathesar/stores/userProfile';
   import DatabaseRoute from './DatabaseRoute.svelte';
   import UserProfileRoute from './UserProfileRoute.svelte';
   import AdminRoute from './AdminRoute.svelte';
   import { getDatabasePageUrl } from './urls';
 
-  export let commonData: CommonData;
-
-  $: userProfileStore = setUserProfileStoreInContext(commonData.user);
+  const userProfileStore = getUserProfileStoreFromContext();
   $: userProfile = $userProfileStore;
-  $: if (userProfile.isSuperUser) {
-    // Toggle these lines to test with a mock tag name
-    // setReleasesStoreInContext('1.75.0');
-    setReleasesStoreInContext(commonData.current_release_tag_name);
-  }
 
   $: firstDatabase = $databases.data?.[0];
 </script>
@@ -35,7 +26,7 @@
   <UserProfileRoute />
 </Route>
 
-{#if userProfile.isSuperUser}
+{#if userProfile?.isSuperUser}
   <Route path="/administration/*" firstmatch>
     <AdminRoute />
   </Route>
diff --git a/mathesar_ui/src/routes/RootRoute.svelte b/mathesar_ui/src/routes/RootRoute.svelte
index ce309c96cd..4f72fcddc1 100644
--- a/mathesar_ui/src/routes/RootRoute.svelte
+++ b/mathesar_ui/src/routes/RootRoute.svelte
@@ -1,14 +1,11 @@
 <script lang="ts">
   import { Route } from 'tinro';
   import type { CommonData } from '@mathesar/utils/preloadData';
-  import { setBreadcrumbItemsInContext } from '@mathesar/components/breadcrumb/breadcrumbUtils';
   import ErrorPage from '@mathesar/pages/ErrorPage.svelte';
   import RouteObserver from '@mathesar/components/routing/RouteObserver.svelte';
   import AuthenticatedRoutes from './AuthenticatedRoutes.svelte';
   import AnonymousAccessRoutes from './AnonymousAccessRoutes.svelte';
 
-  setBreadcrumbItemsInContext([]);
-
   export let commonData: CommonData;
 </script>
 
@@ -35,7 +32,7 @@
 {:else}
   <Route path="/*" firstmatch>
     {#if commonData.is_authenticated}
-      <AuthenticatedRoutes {commonData} />
+      <AuthenticatedRoutes />
     {/if}
 
     <Route path="/shares/*">

Release 0.1.3
## 2023-08-16
```[tasklist]
### Tasks
- [x] Cut 0.1.3 release branch, freeze code
- [x] Update version number in all places in the new branch
- [x] Make an image from the branch with tag `0.1.3`, push to Dockerhub
- [x] Test installation with the new image
- [x] Test upgrade
- [x] Smoke testing application
- [x] Stability of the newly released items
```

