Skip to content

Bootstrapping an admin

There is no HTTP path to grant is_admin = TRUE. The first admin (and every subsequent admin) must be set via direct DB access. This is intentional — keeping the promotion path narrow makes social engineering harder and forces the trail to land in the Postgres-level audit log.

UPDATE publishers SET is_admin = TRUE WHERE name = 'your-handle';

What happens automatically:

  • The publishers_mfa_sticky_trg trigger from migration 033 sets mfa_required = TRUE on the same row.
  • The next time that user attempts to log in, the auth flow refuses to issue a session until they’ve enrolled a TOTP or WebAuthn factor at /dashboard/settings/mfa.
  • The MFA flag is sticky — even if you later set is_admin = FALSE, the user must keep using MFA to log in.
UPDATE publishers SET is_admin = FALSE WHERE name = 'your-handle';

Configure Postgres to log every promotion:

# postgresql.conf
log_statement = 'mod' # logs INSERT/UPDATE/DELETE
log_min_duration_statement = 0

Optional but recommended:

  • Enable pg_audit and add publishers to the audit set so promotions write to the cluster-level audit log in addition to audit_logs.
  • Send a webhook to a dedicated #security Slack channel whenever a row in publishers has is_admin flip. A LISTEN/NOTIFY-driven worker is the simplest path.
  • Restrict the singularity_app role from UPDATE publishers SET is_admin = …. Promotions should come from a DBA role with separate credentials.
SELECT id, name, is_admin, mfa_required FROM publishers WHERE is_admin = TRUE;

Then verify the application-level state:

Terminal window
# As the newly-promoted admin, after enrolling MFA + logging in:
curl -H "Cookie: singularity_session=..." \
https://api.singularitymarketplace.com/api/v1/admin/stats

If this returns 401 they aren’t logged in; 403 means they haven’t enrolled MFA yet; 200 means they’re a fully-fledged admin.