# Admin (operator) portal

The admin portal lives at `https://activitylog.com/portal/admin/` and is **internal tooling for Fyin staff** to manage the ActivityLog platform across all tenants. It is not a customer-facing feature, not part of any priced tier, and does not appear in marketing material.

> **If you're a customer reading this**: you don't have access to the admin portal. For customer-side tenant administration (managing your own users, tokens, retention) use the customer portal at `/portal/`. The doc you probably want is [`10-setup-account-tenant.md`](./10-setup-account-tenant.md) or [`30-invites-and-team.md`](./30-invites-and-team.md). If you need an operator action on your tenant (e.g. reset a TOTP on a locked-out admin), contact support and we'll do it from here.

This page documents the admin portal for the Fyin staff who use it.

## Access model

- Admin users are regular portal users with the `admin` role bit set.
- The `admin` role is granted by another admin (bootstrapped by the operator who holds the legacy `Admin <token>` bearer).
- Non-admin users hitting `/portal/admin/*` get a **404** (not 403) — the namespace doesn't leak.
- One role bit (`admin`) in V1; layered roles (`admin:read`, `admin:tenant`, `admin:impersonate`) deferred to V2.

## Session security (admins only)

Tighter than customer sessions:

| Setting | Customer user | Admin user |
|---|---|---|
| Access token TTL | 1 hour | **30 minutes** |
| Refresh token TTL | 30 days | **8 hours** |
| TOTP mandatory | Optional | **Required** (7-day grace from first sign-in) |
| TOTP re-prompt on destructive actions | n/a | **Yes** — every delete / archive / password-reset / TOTP-reset |
| Rate limit | 600 req/min | 60 req/min |

The 8-hour refresh effectively forces daily re-auth. The per-action TOTP re-prompt is a modal that asks for the current 6-digit code; valid for one action and expires immediately on submit.

## TOTP grace + post-grace behavior

When a new admin user is created, they have **7 days** to enroll TOTP. Sign-in works without it during the grace window.

After 7 days without enrollment, sign-in hits a forced-enrollment interstitial — the admin can't reach any admin page until they finish the QR-code flow.

If the user is locked out (lost device + lost backup codes), another admin can extend the grace from `/portal/admin/users/{id}` → **Extend TOTP grace** (max 14 days from now, requires a `reason`).

## Capabilities (V1)

The admin portal write-side is **in flight as of 2026-05**. Capabilities below are spec-locked; check the Feature-Audit ([`../Feature-Audit.md`](../Feature-Audit.md)) for the per-commit state.

### User management

| Capability | Notes |
|---|---|
| List + search users | Across all tenants |
| View user detail | Email, tenants, roles, last sign-in, TOTP state |
| Create user | Operator-driven (skip email-verify) |
| Lock / unlock user | Doesn't delete; suspends sign-in |
| Delete user (anonymize) | Soft-delete: email anonymized, audit retained |
| Password reset | Generates a one-time reset link; admin copies and conveys to user |
| Reset TOTP | Clears secret + sets `TotpEnabled=false` + resets grace to now+7d |
| Extend TOTP grace | Max 14 days; requires reason |

Destructive actions (delete user, reset TOTP, reset password) trigger the TOTP re-prompt.

### Tenant management

| Capability | Notes |
|---|---|
| List + search tenants | All tenants in the platform |
| View tenant detail | Members, systems, usage rollup |
| Create tenant | Operator-driven; assign owner |
| Edit tenant | Name, tier, retention override, rate-limit override |
| Schedule tenant deletion | Two-stage (see below) |

### Two-stage tenant delete

A tenant deletion is **two-step**, on purpose:

1. **Schedule deletion** — sets `DeletionScheduledAtUtc = now + grace`. The tenant is marked for deletion but still functional. Members see a banner.
   - **Grace**: 7 days for Free/Pro, 30 days for Enterprise.
2. **Hard delete** — automatic at the scheduled time, or manual via **Delete now** (operator-driven, requires re-typing the tenant name + TOTP re-prompt).

Until step 2 fires, the tenant can be restored by another operator click. Ingest continues; query continues. The banner is the only customer-visible change.

This avoids a "click oops" wiping a real customer's data. Same pattern as Momentus.

![Tenant detail showing scheduled deletion banner](screenshots/admin-portal-tenant-scheduled-delete.png)

### System + token management

Operator-side counterparts of the customer portal flows:

| Capability | Notes |
|---|---|
| List systems across tenants | For support investigation |
| Inspect system tokens | Hashes only — never the raw value |
| Force-revoke a token | When a customer reports a leak |
| Change retention on a token | Beyond what the customer-side dropdown allows (e.g. extending an Enterprise customer to 7 years for compliance) |

### Audit trail

Every operator action lands in the platform audit log as a `Message` row on the synthetic `__audit` system. Filter by:

- **Actor** — which operator
- **Action** — `user.delete`, `tenant.schedule-delete`, `token.revoke`, etc.
- **Target** — the affected user / tenant / token
- **Time range**

CSV export is available from the Audit tab (V1 required — compliance use case).

![Audit log with actor and action filters](screenshots/admin-portal-audit-log.png)

### Activity dots

The activity-feed chart (live since 2026-04) shows cross-tenant activity as colored dots categorized by event type. Used as the "is the platform healthy?" at-a-glance check.

| Element | What it shows |
|---|---|
| Color | Event class (ingest / query / auth / admin / error) |
| Vertical position | Per-tenant lane |
| Density | Volume |
| Hover | Per-dot detail (timestamp, system, type) |

![Admin activity-dots chart](screenshots/admin-portal-activity-dots.png)

## What's not in V1

| Capability | Status |
|---|---|
| `admin:impersonate` (sign in as a customer user) | V2 — wait until support volume justifies |
| Layered admin roles | V2 |
| Customer email on operator mutations | Deferred until email infra lands (Phase β) |
| In-portal billing / invoice mutations | Out of scope for V1 |

## Bootstrap

The first admin user is created via a one-time `POST /api/v1/admin/users/bootstrap-first-admin` endpoint, authed with the legacy `Admin <token>` bearer. The endpoint self-disables after the first successful call.

Subsequent admin creations go through the regular **Create user → assign `admin` role** flow.

## What's next

| Goal | Doc |
|---|---|
| Full spec | [`../Admin-Portal.md`](../Admin-Portal.md) |
| Three-principal auth architecture | [`../Architecture.md` § 5](../Architecture.md) |
| TOTP UX from the customer side | [`40-totp-setup.md`](./40-totp-setup.md) |

## For customers

If you're a customer who landed here looking for help with **your own tenant administration**, you want:

- [`10-setup-account-tenant.md`](./10-setup-account-tenant.md) — account model and roles
- [`20-setup-system-tokens.md`](./20-setup-system-tokens.md) — token management
- [`30-invites-and-team.md`](./30-invites-and-team.md) — adding teammates
- [`40-totp-setup.md`](./40-totp-setup.md) — two-factor auth

If you need an action that only an operator can perform (TOTP reset on a locked-out admin, recovery from a critical mistake), open a support ticket.
