Href Creative Docs
HREF Team Docs

Shadow mode

How Href admins view a tenant's CRM read-only without disturbing data.

When you click Open CRM (shadow) from a tenant detail page in the admin console, you launch a shadow session: an authenticated, read-only view of that tenant's CRM as if you were one of their team members.

How it works

  1. The action ensures a tenant_members row exists for you on that tenant with role = 'shadow'. If you're already a real member (admin/manager/member), the existing role is preserved and shadow mode does not activate (you'd see the CRM normally).
  2. Supabase Auth generates a one-time magic link aimed at the tenant's /auth/callback. The admin console opens that link in a new tab.
  3. Once the magic link lands, you have a session on the tenant subdomain. Cookies are scoped to that subdomain only; your admin-console session stays put.
  4. The CRM layout detects role = 'shadow' and renders an amber Shadow mode banner at the top with your admin email and an Exit shadow button.

What "read-only" means

Every server action that mutates tenant data (stage moves, note adds, task changes, settings edits, invites, API key rotations, etc.) calls requireWriteAccess(). That helper enforces role !== 'shadow'. If you click an edit control in shadow mode, you'll get a toast:

Read-only: you're shadowing this workspace as an Href admin.

UI controls aren't hidden — they're visible so you can see what the user sees — but they won't do anything. Phase 2 polish will hide them entirely.

Exiting

Click Exit shadow in the banner. You're signed out of the tenant subdomain; your admin-console session is unaffected (different cookie scope). Back to admin.

The shadow tenant_members row stays so re-entering shadow on the same tenant is instant. To fully clean up after support is done, ask an admin to remove the row via SQL:

DELETE FROM tenant_members
WHERE user_id = '<admin-user-id>' AND role = 'shadow';

Audit notes

Shadow sessions show up the same way as regular sessions in Supabase Auth logs. There is no separate "shadow event" table yet. v2 will add an admin_audit_log that records every shadow entry, every edit attempted (and rejected), and every tenant rename / delete from the admin console.

Implementation reference

  • Schema: 0004_shadow_role.sql adds 'shadow' to the tenant_members.role CHECK.
  • Server-side gate: src/lib/auth/session.ts → requireWriteAccess().
  • Launch action: src/lib/actions/shadow.ts → startShadowSession().
  • Banner: src/components/shadow-banner.tsx, wired into src/app/(auth)/crm/layout.tsx.

On this page