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:
- Update the env value (in Azure: Container App → Configuration →
Environment variables, or via bicep param
marketplaceAdminGhLoginsif you're managing it through the deploy workflow). - Save → new revision.
- 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:
- Deprecate —
POST /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
deletedAton 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 themarketplace_listingsrow (reviewer_user_id,reviewed_at,reviewer_notes). - Listing edits + version submissions are recorded in
marketplace_versionswith the publisher's id + timestamp. - There's no UI for the audit log yet — query the DB directly when you need it.