"]
+license = "AGPLv3"
+readme = "README.md"
+homepage = "https://git.autonomic.zone/autonomic-cooperative/magic-app"
+repository = "https://git.autonomic.zone/autonomic-cooperative/magic-app"
+documentation = "https://git.autonomic.zone/autonomic-cooperative/magic-app"
+keywords = ["docker", "swarm", "packaging"]
+
+[tool.poetry.dependencies]
+"ruamel.yaml" = "^0.16.10"
+celery = "^4.4.6"
+flask = "^1.1.2"
+flask-wtf = "^0.14.3"
+gunicorn = "^20.0.4"
+python = "^3.8"
+redis = "^3.5.3"
+
+[tool.poetry.dev-dependencies]
+black = "^19.10b0"
+fakeredis = "^1.4.1"
+flake8 = "^3.8.3"
+isort = "^5.0.2"
+
+[tool.poetry.urls]
+issues = "https://git.autonomic.zone/autonomic-cooperative/magic-app/issues"
+
+[tool.black]
+line-length = 80
+target-version = ["py38"]
+include = '\.pyi?$'
+
+[build-system]
+requires = ["poetry>=1.0.9,<2.0"]
+build-backend = "poetry.masonry.api"
+
+[tool.tox]
+legacy_tox_ini = """
+[tox]
+envlist = py38 lint sort format type
+skip_missing_interpreters = True
+isolated_build = True
+
+[testenv]
+
+[testenv:lint]
+skipdist = True
+deps = flake8
+commands = flake8 {posargs:--max-line-length 80} magic_app/ test/
+
+[testenv:sort]
+skipdist = True
+deps = isort
+commands = isort {posargs:-c} -sp setup.cfg magic_app/ test/
+
+[testenv:format]
+skipdist = True
+deps = black
+commands = black {posargs:--check} magic_app/ test/
+
+[testenv:type]
+skipdist = True
+deps = mypy
+commands = mypy {posargs:--ignore-missing-imports} magic_app/ test/
+
+[testenv:release]
+deps = twine
+commands =
+ rm -rf {toxworkdir}/dist
+ python -m setup sdist --dist-dir {toxworkdir}/dist bdist_wheel
+ python -m setup sdist --dist-dir {toxworkdir}/dist bdist_egg
+ twine upload {toxworkdir}/dist/*
+whitelist_externals =
+ rm
+"""
+
+[tool.isort]
+include_trailing_comma = true
+known_first_party = "magic_app"
+known_third_party = "pytest"
+line_length = 80
+multi_line_output = 3
+skip = ".venv,.tox"
diff --git a/scripts/__init__.py b/scripts/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/scripts/celworker.py b/scripts/celworker.py
new file mode 100644
index 0000000..e346e31
--- /dev/null
+++ b/scripts/celworker.py
@@ -0,0 +1,9 @@
+"""Bootstrap the application for Celery."""
+from os import environ
+
+from magic_app.app import celery, create_app # noqa
+from magic_app.config import CONFIG
+
+config = CONFIG[environ["FLASK_ENV"]]
+app = create_app(config=config)
+app.app_context().push()
diff --git a/scripts/wsgi.py b/scripts/wsgi.py
new file mode 100644
index 0000000..7c2e3cb
--- /dev/null
+++ b/scripts/wsgi.py
@@ -0,0 +1,8 @@
+"""Bootstrap the application for web serving."""
+import os
+
+from magic_app.app import create_app
+from magic_app.config import CONFIG
+
+config = CONFIG[os.environ["FLASK_ENV"]]
+app = create_app(config=config)
diff --git a/spikes/README.md b/spikes/README.md
new file mode 100644
index 0000000..85401db
--- /dev/null
+++ b/spikes/README.md
@@ -0,0 +1,27 @@
+# Run It
+
+```bash
+$ python3 -m venv .venv
+$ source .venv/bin/activate
+$ pip install -r requirements.txt
+$ python first.py / second.py / third.py
+```
+
+## first.py
+
+- List apps from an `app.json` (points to https://git.autonomic.zone/compose-stacks)
+- Clone selected app template and parse configuration for inputs (env vars and secrets)
+- Generate a form so those values can be filled out and allow it to be saved
+- Save the form inputs to a `db.json` (as a start)
+- Deploy an applicaiton to a local swarm (assumes access to local docker socket)
+- Create an "edit app" page where the `db.json` is re-called and can be updated
+- Make sure re-deploy works (taking care of updating secret and app versions)
+
+## second.py
+
+- Don't try to be smart with the auto-generation, hard-code everything. We
+ maintain the app template (`compose.yml`) and this code anyway, so we just
+ need to be aware of each other and keep in sync. This would optimise for
+ trust and collaboration and not "smart" code.
+- Hard-code the secrets/configs required to make the code for generating
+ versions simpler as well.
diff --git a/first.py b/spikes/first.py
similarity index 100%
rename from first.py
rename to spikes/first.py
diff --git a/requirements.txt b/spikes/requirements.txt
similarity index 100%
rename from requirements.txt
rename to spikes/requirements.txt
diff --git a/second.py b/spikes/second.py
similarity index 100%
rename from second.py
rename to spikes/second.py
diff --git a/static/apps.json b/spikes/static/apps.json
similarity index 100%
rename from static/apps.json
rename to spikes/static/apps.json
diff --git a/static/picnic.min.css b/spikes/static/picnic.min.css
similarity index 100%
rename from static/picnic.min.css
rename to spikes/static/picnic.min.css
diff --git a/templates/base.html b/spikes/templates/base.html
similarity index 100%
rename from templates/base.html
rename to spikes/templates/base.html
diff --git a/templates/first/index.html b/spikes/templates/first/index.html
similarity index 100%
rename from templates/first/index.html
rename to spikes/templates/first/index.html
diff --git a/templates/first/install.html b/spikes/templates/first/install.html
similarity index 100%
rename from templates/first/install.html
rename to spikes/templates/first/install.html
diff --git a/templates/second/deploy.html b/spikes/templates/second/deploy.html
similarity index 100%
rename from templates/second/deploy.html
rename to spikes/templates/second/deploy.html
diff --git a/templates/second/index.html b/spikes/templates/second/index.html
similarity index 100%
rename from templates/second/index.html
rename to spikes/templates/second/index.html
diff --git a/templates/second/install.html b/spikes/templates/second/install.html
similarity index 100%
rename from templates/second/install.html
rename to spikes/templates/second/install.html
diff --git a/spikes/templates/second/macros.html b/spikes/templates/second/macros.html
new file mode 100644
index 0000000..518cb1e
--- /dev/null
+++ b/spikes/templates/second/macros.html
@@ -0,0 +1,16 @@
+{#
+ From: https://wtforms.readthedocs.io/en/2.3.x/specific_problems/#rendering-errors
+ Usage: with_errors(form.field, style='font-weight: bold')
+#}
+
+{% macro with_errors(field) %}
+
+{% endmacro %}
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 0000000..e69de29