diff --git a/docs/changelog.rst b/docs/changelog.rst
index 7e6be33..4aeb686 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,6 +1,8 @@
 Changelog
 =========
 
+* :feature:`-` add support model for offline account reset codes in new help
+  app
 * :support:`-` remove unused PowerDNS support tables from domains app
 * :feature:`-` add impersonation support for superusers
 * :support:`-` remove django-braces dependency
diff --git a/gnuviechadmin/gnuviechadmin/settings.py b/gnuviechadmin/gnuviechadmin/settings.py
index 243b6e8..089e171 100644
--- a/gnuviechadmin/gnuviechadmin/settings.py
+++ b/gnuviechadmin/gnuviechadmin/settings.py
@@ -232,6 +232,7 @@ LOCAL_APPS = (
     "userdbs",
     "hostingpackages",
     "websites",
+    "help",
     "contact_form",
 )
 
@@ -276,7 +277,7 @@ LOGGING = {
     "formatters": {
         "verbose": {
             "format": "%(levelname)s %(asctime)s %(name)s "
-                      "%(module)s:%(lineno)d %(process)d %(thread)d %(message)s"
+            "%(module)s:%(lineno)d %(process)d %(thread)d %(message)s"
         },
         "simple": {"format": "%(levelname)s %(name)s:%(lineno)d %(message)s"},
     },
@@ -404,23 +405,8 @@ if GVA_ENVIRONMENT == "local":
         dict(
             [
                 (key, {"handlers": ["console"], "level": "DEBUG", "propagate": True})
-                for key in [
-                "dashboard",
-                "domains",
-                "fileservertasks",
-                "gvacommon",
-                "gvawebcore",
-                "hostingpackages",
-                "ldaptasks",
-                "managemails",
-                "mysqltasks",
-                "osusers",
-                "pgsqltasks",
-                "taskresults",
-                "userdbs",
-                "websites",
-            ]
-            ]
+                for key in LOCAL_APPS
+            ],
         )
     )
 elif GVA_ENVIRONMENT == "test":
@@ -439,22 +425,7 @@ elif GVA_ENVIRONMENT == "test":
         dict(
             [
                 (key, {"handlers": ["console"], "level": "ERROR", "propagate": True})
-                for key in [
-                "dashboard",
-                "domains",
-                "fileservertasks",
-                "gvacommon",
-                "gvawebcore",
-                "hostingpackages",
-                "ldaptasks",
-                "managemails",
-                "mysqltasks",
-                "osusers",
-                "pgsqltasks",
-                "taskresults",
-                "userdbs",
-                "websites",
-            ]
+                for key in LOCAL_APPS
             ]
         )
     )
diff --git a/gnuviechadmin/help/__init__.py b/gnuviechadmin/help/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/gnuviechadmin/help/admin.py b/gnuviechadmin/help/admin.py
new file mode 100644
index 0000000..b143c6c
--- /dev/null
+++ b/gnuviechadmin/help/admin.py
@@ -0,0 +1,22 @@
+from django.contrib import admin
+from django.contrib.auth import get_user_model
+from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
+from django.utils.translation import ugettext_lazy as _
+
+from help.models import HelpUser
+
+User = get_user_model()
+
+
+class HelpUserInline(admin.StackedInline):
+    model = HelpUser
+    can_delete = False
+    readonly_fields = ("offline_account_code",)
+
+
+class UserAdmin(BaseUserAdmin):
+    inlines = (HelpUserInline,)
+
+
+admin.site.unregister(User)
+admin.site.register(User, UserAdmin)
diff --git a/gnuviechadmin/help/apps.py b/gnuviechadmin/help/apps.py
new file mode 100644
index 0000000..870ccf9
--- /dev/null
+++ b/gnuviechadmin/help/apps.py
@@ -0,0 +1,8 @@
+from django.apps import AppConfig
+from django.utils.translation import gettext_lazy as _
+
+
+class HelpConfig(AppConfig):
+    default_auto_field = "django.db.models.BigAutoField"
+    name = "help"
+    verbose_name = _("User self help")
diff --git a/gnuviechadmin/help/management/__init__.py b/gnuviechadmin/help/management/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/gnuviechadmin/help/management/commands/__init__.py b/gnuviechadmin/help/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/gnuviechadmin/help/management/commands/populate.py b/gnuviechadmin/help/management/commands/populate.py
new file mode 100644
index 0000000..77ee7c3
--- /dev/null
+++ b/gnuviechadmin/help/management/commands/populate.py
@@ -0,0 +1,17 @@
+from django.contrib.auth import get_user_model
+from django.core.management import BaseCommand
+
+from help.models import HelpUser
+
+User = get_user_model()
+
+
+class Command(BaseCommand):
+    help = "Populate help user information for existing users"
+
+    def handle(self, *args, **options):
+        for user in User.objects.filter(helpuser=None):
+            help_user = HelpUser.objects.create(user_id=user.id, email_address=user.email)
+            help_user.generate_offline_account_code()
+            help_user.save()
+            self.stdout.write(f"created offline account code {help_user.offline_account_code} for {user}.")
diff --git a/gnuviechadmin/help/management/commands/reset_offline_code.py b/gnuviechadmin/help/management/commands/reset_offline_code.py
new file mode 100644
index 0000000..a93579d
--- /dev/null
+++ b/gnuviechadmin/help/management/commands/reset_offline_code.py
@@ -0,0 +1,29 @@
+from django.contrib.auth import get_user_model
+from django.core.management import BaseCommand, CommandError
+
+from help.models import HelpUser
+
+User = get_user_model()
+
+
+class Command(BaseCommand):
+    help = "Reset offline account reset code for existing users"
+
+    def add_arguments(self, parser):
+        parser.add_argument("users", nargs='+', type=str)
+
+    def handle(self, *args, **options):
+        for name in options["users"]:
+            try:
+                user = User.objects.get(username=name)
+            except User.DoesNotExist:
+                raise CommandError(f'User {name} does not exist')
+
+            help_user = user.helpuser
+            if help_user is None:
+                help_user = HelpUser.objects.create(email_address=user.email)
+
+            help_user.generate_offline_account_code()
+            help_user.save()
+
+            self.stdout.write(f"generated new offline account reset code {help_user.offline_account_code} for {name}")
diff --git a/gnuviechadmin/help/migrations/0001_add_help_user_model_extension.py b/gnuviechadmin/help/migrations/0001_add_help_user_model_extension.py
new file mode 100644
index 0000000..fb20210
--- /dev/null
+++ b/gnuviechadmin/help/migrations/0001_add_help_user_model_extension.py
@@ -0,0 +1,55 @@
+# Generated by Django 3.2.18 on 2023-04-16 09:32
+
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    initial = True
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name="HelpUser",
+            fields=[
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                (
+                    "email_address",
+                    models.EmailField(
+                        help_text="Contact email address", max_length=254
+                    ),
+                ),
+                (
+                    "postal_address",
+                    models.TextField(blank=True, help_text="Contact postal address"),
+                ),
+                (
+                    "offline_account_code",
+                    models.CharField(
+                        default="",
+                        help_text="Offline account reset code",
+                        max_length=36,
+                    ),
+                ),
+                (
+                    "user",
+                    models.OneToOneField(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        to=settings.AUTH_USER_MODEL,
+                    ),
+                ),
+            ],
+        ),
+    ]
diff --git a/gnuviechadmin/help/migrations/__init__.py b/gnuviechadmin/help/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/gnuviechadmin/help/models.py b/gnuviechadmin/help/models.py
new file mode 100644
index 0000000..5debb7e
--- /dev/null
+++ b/gnuviechadmin/help/models.py
@@ -0,0 +1,17 @@
+import uuid
+
+from django.conf import settings
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+
+class HelpUser(models.Model):
+    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
+    email_address = models.EmailField(help_text=_("Contact email address"))
+    postal_address = models.TextField(help_text=_("Contact postal address"), blank=True)
+    offline_account_code = models.CharField(
+        help_text=_("Offline account reset code"), max_length=36, default=""
+    )
+
+    def generate_offline_account_code(self):
+        self.offline_account_code = str(uuid.uuid4())
diff --git a/gnuviechadmin/help/tests.py b/gnuviechadmin/help/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/gnuviechadmin/help/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/gnuviechadmin/help/views.py b/gnuviechadmin/help/views.py
new file mode 100644
index 0000000..91ea44a
--- /dev/null
+++ b/gnuviechadmin/help/views.py
@@ -0,0 +1,3 @@
+from django.shortcuts import render
+
+# Create your views here.