---
title: Admin Dashboard & Team Access
description: Run branding, analytics, AI, and team access from a live dashboard — no rebuild.
url: http://localhost:3040/guides/admin-dashboard
---

# Admin Dashboard & Team Access

Run branding, analytics, AI, and team access from a live dashboard — no rebuild.

Thally ships with a built-in **admin dashboard** at `/admin`. It lets you control your docs site at runtime — change branding, toggle features, read analytics, manage your team, and protect the docs behind a password — without editing files or redeploying. Settings chosen here are stored at runtime and layered *on top of* the git-committed defaults in `docs.json` and `src/data/site.ts`.

## What you can do

| Section | What it controls |
| --- | --- |
| **Home** | At-a-glance overview of the site's status. |
| **Analytics** | First-party traffic, top pages, search terms, and content gaps. On serverless hosts this needs a remote database — see [Analytics storage](#analytics-storage). |
| **Agent Readiness** | Score for how well your docs serve AI agents. |
| **Docs tasks** | Documentation edits drafted by the docs agent as pull requests — the [Thally Track](/guides/thally-track) queue. |
| **MCP server** | Enable or disable the public `/api/mcp` endpoint. |
| **Team** | Members, domains, and roles for admin access. |
| **Branding** | Structural theme, brand accent, per-mode logos and favicons (see [Branding and theming](/guides/branding-and-theming)). |
| **Settings** | Feature toggles, site identity, the AI assistant name & disclaimer, docs-access password, the AI chat key, and **Connect GitHub** for Thally Track. |

Everything an Owner changes here applies to the live site immediately — no merge, no rebuild.

## Turning on the dashboard

You grant access by configuring one of the two methods below. Do this **before you deploy**.

If you deploy with **neither** `THALLY_ADMIN_PASSWORD` nor OIDC set, Thally does not gate `/admin` — anyone who reaches the URL gets full **Owner** access, in production too. There is no localhost-only fallback. Always configure one of the methods below before your site goes public.

### Option 1 — Break-glass password (quickest)

Set a single environment variable and sign in with that password. Anyone who knows it gets **Owner** access — best for a solo maintainer or a quick setup.

```bash
THALLY_ADMIN_PASSWORD=a-long-random-string
```

This is a shared secret that grants full control. Use a long, random value and store it in your host's secret manager, not in git.

### Option 2 — Team sign-in with OIDC (recommended for teams)

Delegate identity to your own IdP (Google Workspace, Microsoft Entra, or any OIDC provider). Members sign in with their work account, and their **role** comes from a roster you keep in `docs.json`. Nothing is hosted by Thally, so there is no per-seat cost.

Set these environment variables:

```bash
THALLY_OIDC_ISSUER=https://accounts.google.com
THALLY_OIDC_CLIENT_ID=your-client-id
THALLY_OIDC_CLIENT_SECRET=your-client-secret
THALLY_AUTH_SECRET=another-long-random-string   # signs sessions & encrypts stored secrets
```

In your IdP, register the callback (redirect) URL:

```
https://your-docs-domain.com/api/admin/auth/callback
```

Thally uses OIDC discovery and requests the `openid email profile` scopes, with PKCE. On the [Google Cloud console](https://console.cloud.google.com/apis/credentials) or your provider's app registration, create an OAuth client and paste its ID and secret into the variables above.

If both a password and OIDC are configured, team members sign in with their identity and the password stays available as a break-glass fallback.

## Roles and the roster

With OIDC enabled, who gets in — and at what level — is defined by a `team` block in `docs.json`. It is version-controlled and code-reviewed, so team management needs no database, even on serverless hosts.

```json
"team": {
  "members": [
    { "email": "you@company.com", "role": "owner" },
    { "email": "writer@company.com", "role": "editor" }
  ],
  "domains": [
    { "domain": "company.com", "role": "viewer" }
  ]
}
```

- **`members`** — explicit email → role. Explicit members always win over domain defaults.
- **`domains`** — anyone who signs in with a verified work email on one of these domains gets the default role.
- Anyone not matched by a member or a domain gets **no access**.

Thally re-checks the roster on every admin page load, so removing or downgrading someone in `docs.json` takes effect on their next request — no waiting for a session to expire.

### What each role can do

| Capability | Viewer | Editor | Owner |
| --- | :---: | :---: | :---: |
| View analytics & agent readiness | ✓ | ✓ | ✓ |
| Manage docs / run the docs agent | | ✓ | ✓ |
| Set the docs-access password | | ✓ | ✓ |
| Change branding & site identity | | | ✓ |
| Manage the team roster | | | ✓ |

The **Team** page shows the current members and domains, and generates the JSON snippet to paste into `docs.json` when you invite someone. Editing the roster itself is a git change — Owners can't add members from the UI, which keeps access under code review.

## Protecting the docs for visitors

Team access above is about the **admin dashboard**. To gate the public docs themselves behind a shared password, an Editor or Owner sets a **docs-access password** under **Settings → Access & authentication**. Visitors are then prompted for it before they can read any page. The password is stored as a salted hash — never in plaintext, never returned by the API.

This is independent of the admin team: a visitor password protects reading the docs; the team roster protects managing them.

## Live overrides vs. file defaults

Anything you set in the dashboard is a runtime override that layers over the committed config:

| Set in the dashboard | Falls back to |
| --- | --- |
| Structural theme, brand accent | `docs.json` `theme`, `src/data/site.ts` brand |
| Logo, favicon | Built-in Thally mark |
| Site name, description, repo URL | `src/data/site.ts` `siteConfig` |
| AI chat on/off + Anthropic key | `docs.json` `ai.chat`, `ANTHROPIC_API_KEY` |
| Analytics on/off | `docs.json` `analytics.enabled` |
| MCP endpoint on/off | On |
| Docs-access password | Open (no password) |

Leave a setting untouched in the dashboard and the file default applies. This lets you keep a sensible, source-controlled baseline for fresh deploys while making day-to-day changes without shipping code.

## Analytics storage

Locally, analytics and admin state write to on-disk SQLite files under `.data/` — zero config. On **serverless** hosts (for example Vercel), that filesystem does not persist, so Home metrics stay empty and Analytics may show an error until you point Thally at a remote libSQL database (Turso).

Set `THALLY_ANALYTICS_DB_URL` / `THALLY_ANALYTICS_DB_TOKEN` for traffic data, and `THALLY_DATABASE_URL` / `THALLY_DATABASE_TOKEN` for durable admin settings and related state. Full setup steps are in [Deploying → Analytics & durable storage](/guides/deploying#analytics--durable-storage).

Agent Readiness does not use this database — it scores your docs content directly.

## AI chat key

The in-docs AI assistant needs an Anthropic API key. Set it either as the `ANTHROPIC_API_KEY` environment variable or, for a no-deploy change, paste it under **Settings → AI chat** — the dashboard stores it encrypted. See [AI features](/guides/ai-features) for the full assistant setup.

## Naming the assistant

Under **Settings → Site experience** you can rename the AI assistant and set its disclaimer, live:

- **Assistant name** — shown on the chat button and panel header (e.g. "Ask Acme"). Blank falls back to the `docs.json` label, or "Ask AI".
- **Assistant disclaimer** — the notice at the foot of the chat panel. Blank uses a generic "answers may be inaccurate, verify important details" message.

Both are stored at runtime and apply without a redeploy.

## Connect GitHub (Thally Track)

Under **Settings → Integrations**, an Owner can click **Connect GitHub** to create and install a GitHub App for [Thally Track](/guides/thally-track) — the org-wide access path that lets the docs agent watch selected private repos and draft docs PRs. It needs `THALLY_AUTH_SECRET` set (to encrypt the app's key). The status chip shows the connected app and whether it's installed; **Disconnect** clears the stored credentials.