Skip to main content

First-time bootstrap

There's a chicken-and-egg problem at the start of every GreekManage deployment: the platform admin UI is gated by the platform admin role, but the role can only be granted by an existing platform admin or by a server-side command. Until at least one admin exists, there's no way to use the UI.

This page documents the one-time bootstrap path — how the first platform admin gets created from a clean install — and the recovery path if you lose your last admin and need to start over.

When you need this page

  • You've just deployed GreekManage for the first time and need to sign in.
  • You're standing up a separate staging or dev environment and need an admin in it.
  • You've accidentally removed your last active platform admin (the sole-admin guard normally prevents this, but a direct DB edit or migration could create the situation).
  • You're rotating the founding admin to a new owner and need a clean handoff.

If you already have at least one working platform admin and just need to add another, use the UI flow at Managing platform admins instead — it's faster and leaves an audit trail.

The chicken-and-egg problem

Three constraints intersect:

  1. The Add Platform Admin form is gated. Only a signed-in platform admin can use it.
  2. SSO doesn't auto-create accounts. Even when an org has SSO configured, the platform itself has no SSO IdP, and SSO sign-ins don't provision new users. So you can't simply sign in via a corporate SSO and become a platform admin.
  3. Self-serve sign-up doesn't grant the role. New accounts created through the standard auth flow are plain User records with no privilege.

The escape hatch is a bundled management command — a server-side script that creates the PlatformAdmin row directly, bypassing the UI gate.

Prerequisites

Before running the bootstrap command, decide:

  • What email the first admin will use. This is the email they'll sign in with and the email used for password resets and notifications. Pick a long-lived address — admin@yourdomain.com or platform-ops@yourdomain.com are better than an individual's address.
  • What title to label them with, if any. Free text, informational only.
  • What password the user account should have. You can either pre-create the user account with a password and then promote it, or rely on the password-reset flow once email is working.

You also need:

  • Shell access to the backend container (or wherever Django management commands run in your deployment).
  • A Django shell or manage.py runner reachable from that shell.
  • The database to be migrated and reachable (the command writes to it).

Step-by-step: create the first platform admin

The bootstrap requires two phases: create the user account, then promote it to platform admin.

Phase 1 — create the user account

The bootstrap command promotes an existing user; it does not create one. You'll need a User record first.

There are two ways to create the user:

Option A — use the sign-up flow (preferred if you have email working).

  1. Hit the sign-in screen at your deployed URL.
  2. Click "Request account" or use the public sign-up flow.
  3. Complete the email verification.

This gives you a normal user account with a known password.

Option B — create the user via Django shell (when email isn't working yet, which is common on first deploy):

From inside the backend container:

python manage.py shell

In the shell:

from django.contrib.auth import get_user_model
User = get_user_model()
User.objects.create_user(
email="admin@yourdomain.com",
password="a-strong-temporary-password",
first_name="Platform",
last_name="Admin",
)

Exit the shell. You now have a User record you can sign in with.

Phase 2 — promote the user to platform admin

From the same backend container:

python manage.py create_platform_admin --email admin@yourdomain.com --title "Founder"

Required argument:

  • --email — the email address of the user to promote. The user account must already exist (Phase 1).

Optional argument:

  • --title — free-text label for the new platform admin.

On success, the command prints:

Successfully created platform admin for admin@yourdomain.com

On failure, the command will say why:

  • No user found with email: admin@yourdomain.com — Phase 1 didn't actually create the user, or the email differs by typo or case.
  • User admin@yourdomain.com is already a platform admin — already done.

Phase 3 — verify

Sign in at your deployed URL with the user's email and password. You should land on /platform/dashboard with the cross-tenant counts (organizations, chapters, members, platform admins, module adoption).

If the dashboard 404s or you land on a member view, the promotion didn't take effect:

  • Confirm the PlatformAdmin row exists by re-running the command — it will fail with "already a platform admin" if it does.
  • Confirm you're signing in with the same email you promoted (watch for casing).
  • Clear your browser cookies and re-sign in.

You can also verify from the Django admin if it's exposed in your environment: /admin/platform/platformadmin/ will list the platform admin records.

Step-by-step: post-bootstrap setup

Platform dashboard immediately after a successful bootstrap — empty org list, ready for setup. Platform dashboard immediately after a successful bootstrap — empty org list, ready for setup.

Once you have a working platform admin and can sign in, do these in order so the rest of onboarding doesn't pile up later.

1. Configure the platform email backend

Without email, your future invites won't deliver and your users can't reset passwords. Pick a provider, paste credentials, save, and send a test email.

Email config.

2. Add a second platform admin

Single-admin platforms are one absent person away from re-bootstrapping. Add a coworker or a shared ops account as a second admin.

Managing platform admins.

3. Enroll a passkey on your account

GreekManage doesn't enforce 2FA, but passkeys are the strong-auth option. Enroll one before you handle any sensitive operations.

Account security → passkeys.

4. Create the first organization

With email working and a second admin in place, provision your first tenant.

Creating a new organization.

5. Configure backups

The default pg_dump schedule presets exist but schedule_enabled may be off out of the box. Confirm backup storage is wired up and a manual backup succeeds before any real data lands in the platform.

Backups & export.

Recovery: lost the last platform admin

The sole-admin guard in the UI normally prevents you from removing your last active platform admin. But there are paths that can leave you locked out:

  • A direct database DELETE against the platform_admin table.
  • A migration error that wiped seed data.
  • An accidental DROP and partial restore.

To recover, repeat the bootstrap procedure:

  1. From the backend container, run python manage.py shell and confirm whether any User records exist:
    from apps.platform.models import PlatformAdmin
    PlatformAdmin.objects.count() # should be 0 if you're locked out
  2. Find or create a User record (Phase 1 above).
  3. Run python manage.py create_platform_admin --email <that user's email>.
  4. Sign in.

There is no need to drop the database or restore from a snapshot to recover from this state — the bootstrap command always works as long as you have shell access.

SSO does not auto-create accounts

A common misunderstanding: "Can I just sign in with my company SSO and become a platform admin?" No.

  • The platform itself has no SSO IdP. SSO (SAML or OAuth) is configured per organization, not platform-wide. The sign-in screen for the platform admin surface uses email + password.
  • SSO sign-ins on tenants do not provision new users. When a tenant has SSO configured and a member signs in for the first time, the SSO flow expects to find an existing user that matches by email. It does not create the user from scratch.

Implications:

  • The very first platform admin must use email + password during bootstrap.
  • Subsequent platform admins also sign in with email + password unless and until platform-level SSO is added (not on the current roadmap).
  • Don't rely on "we'll just SSO in" as your bootstrap plan.

If your security policy requires strong auth for platform admins, the right path is to enroll passkeys on each platform admin's account. Passkeys work on the email-plus-password sign-in screen as the second factor.

Operational notes

Where the management command lives

The command is bundled into the backend container and runs against the live database. There's no separate installer or admin-tool image to pull — if you can run python manage.py <anything> you can run python manage.py create_platform_admin.

Running it from a Django shell

The Django shell works too — sometimes more convenient if you're already in there for Phase 1:

from django.core.management import call_command
call_command("create_platform_admin", email="admin@yourdomain.com", title="Founder")

Same effect.

Running it against staging vs production

The command operates on whatever database the backend container is currently configured to point at. There's no --env flag or --database flag. Make sure you're in the right container before running.

Auditing the bootstrap event

The bootstrap command writes to the PlatformAdmin table directly and does not write an audit log entry (the audit middleware is HTTP-request-scoped; CLI commands sidestep it). If your compliance regime requires bootstrap evidence, capture the shell history and command output separately — for example, paste the success line into a runbook ticket.

Errors and edge cases

No user found with email: ... — Phase 1 didn't produce a user, or the email differs (case, typo, trailing whitespace). User.objects.filter(email__iexact=...).exists() in the shell will tell you whether the user actually exists under a different casing.

User ... is already a platform admin. — The row already exists. Sign in and check the admins list. If the row is marked inactive (is_active = false), engineering will need to flip it back; the bootstrap command refuses to re-create an existing admin.

Command exits silently, no output. — Almost certainly an import error before main(). Run python manage.py check to verify Django can import cleanly. If check itself fails, that's a deployment-config problem, not a bootstrap problem.

Sign-in fails after bootstrap with "Invalid credentials." — Wrong password (most common — verify what you actually set in Phase 1), or you used a different email casing than the user record. Reset the password via the shell:

from django.contrib.auth import get_user_model
u = get_user_model().objects.get(email="admin@yourdomain.com")
u.set_password("new-temporary-password")
u.save()

Sign-in succeeds but dashboard 404s. — The platform admin record exists but probably for a different user (different email casing, or a stale row). Verify with:

from apps.platform.models import PlatformAdmin
list(PlatformAdmin.objects.values_list("user__email", flat=True))

Troubleshooting

SymptomLikely causeFix
No user foundUser wasn't created in Phase 1Create the user via shell or sign-up flow
User is already a platform adminAlready doneJust sign in
Command not foundBackend image too oldRebuild and redeploy to current version
Sign-in says "Invalid credentials"Wrong passwordReset via u.set_password() in shell
Dashboard 404 after sign-inPromoted a different user than the one signing inCheck email casing; promote the right one
Can't run shellContainer restarting / unhealthyCheck container logs; fix the underlying issue first

Tips

  • Don't bootstrap with an individual's personal email. Use a role-based address that survives the individual leaving.
  • Add a second admin immediately. The whole point of having a guard against removing the last admin is that re-bootstrapping is a recovery operation, not a routine one.
  • Capture the shell session. Keep a record of who ran the command, when, on which environment. Even though it's not auto-audited, the operational record matters.
  • Test bootstrap on staging first. If you're standing up production, prove the procedure on a throwaway environment first so production bootstrap is muscle memory.
  • Set a strong password. Anything you'll later replace with a reset-link-driven password works; just don't leave a weak one in place once email works.

What's not built today

  • A UI-driven bootstrap flow for the very first admin. CLI only.
  • An audit log entry for the bootstrap event. Out-of-band; capture in a runbook ticket.
  • Platform-level SSO for admin sign-in. Email + password (with optional passkey) only.
  • SSO auto-provisioning of users. Existing-account matching only.
  • A --password flag on the bootstrap command. Pre-create the user with a password (Phase 1) and then promote.
  • A --soft / --inactive flag to create an inactive admin record. Active by default.

Last verified against v0.62.1 (2026-05-11).