Admin access

Granting + revoking admin, the grant-only semantics, deprecation + takedown procedures, and where the audit trail lives.

The marketplace has one privilege level above signed-in: admin. Admins see the moderation queue at /admin, can approve / reject submissions, and can deprecate or soft-delete any listing. Everyone else (signed-in or anonymous) sees only the catalog browse + their own publishing dashboard.

Granting admin

Admin permission is granted by the platform operator via the MARKETPLACE_ADMIN_GH_LOGINS env on the marketplace Container App. It's a comma-separated list of GitHub logins, normalised to lowercase:

MARKETPLACE_ADMIN_GH_LOGINS=alice,bob,charlie

On first sign-in, matching logins get isAdmin = true on their marketplace_users row. The Admin link then appears in the auth bar, and they can hit /admin directly.

To add someone:

  1. Update the env value (in Azure: Container App → Configuration → Environment variables, or via bicep param marketplaceAdminGhLogins if you're managing it through the deploy workflow).
  2. Save → new revision.
  3. The next time the new admin signs in, their row picks up the flag.

Revoking admin

The env var only grants — it doesn't revoke. To remove someone:

-- Connect to the marketplace DB (enclaw-marketplace-db-<env>).
UPDATE marketplace_users SET is_admin = false WHERE gh_login = 'alice';

Updating MARKETPLACE_ADMIN_GH_LOGINS and redeploying won't downgrade an existing admin — see the comment in apps/marketplace/src/server/auth/oauth.ts around existing.isAdmin || isAdmin. The grant-only semantics are intentional: if the env list gets cleared by accident (or someone typos), nobody loses access mid-incident.

Deprecation + takedown

If a previously-approved listing is found to contain something nasty (a leaked credential, a malicious dep, an off-policy payload), an admin can:

  • DeprecatePOST /api/marketplace/listings/{id}/deprecate — hides from search; existing installs keep working. Use for "don't recommend this anymore" without breaking deployed tenants.
  • Soft-delete — set deletedAt on the listing row directly via the marketplace DB. Hides from all surfaces. Use only when the listing is actively harmful; tenant installs will still keep their cloned copies but won't see version updates.

There is no "rip out of every tenant" hammer — by design, the marketplace can't reach back into customer tenants once they've installed. Communicate via the tenant operator channel if that's needed.

Audit

  • Every approve / reject is recorded with the reviewer's marketplace_users.id, timestamp, and notes in the marketplace_listings row (reviewer_user_id, reviewed_at, reviewer_notes).
  • Listing edits + version submissions are recorded in marketplace_versions with the publisher's id + timestamp.
  • There's no UI for the audit log yet — query the DB directly when you need it.