<!-- source-hash: b3bbee124a5b530f9a19e9b047eec040 -->
Fetches and surfaces the current chat session's identity tier (`authTier`, `source`, `attachmentsEnabled`, `user`) via a lightweight `useEffect`/`useState` hook — no client-side cache by design.

## Key Components

### Interfaces

- **`ChatIdentityResponse`** — Wire shape mirroring the hub's `/api/auth/identity` route response. Includes `authTier` (`'session' | 'bearer-act-as' | 'anon'`), `source`, `attachmentsEnabled` (server-computed), and a nullable `user` object with optional `firstName`, `lastName`, and `avatarUrl`.
- **`ChatIdentitySurface`** — Extends `ChatIdentityResponse` with an `isLoading` boolean for skeleton/spinner states.

### Hook

- **`useChatIdentity()`** — Reads the identity endpoint URL from `useRequiredChatRuntime()`, fires an authenticated fetch via `chatAuthedFetch`, and returns identity data merged with `isLoading`. Falls back to `ANON_DEFAULTS` on errors or non-OK responses — never throws.

### Constants

- **`ANON_DEFAULTS`** — Safe fallback (`authTier: 'anon'`, `attachmentsEnabled: false`, `user: null`) used during loading and on any fetch failure.

## Usage Example

```typescript
import { useChatIdentity } from './use-chat-identity'

function AttachmentButton() {
  const { isLoading, attachmentsEnabled, user } = useChatIdentity()

  if (isLoading) return <Spinner />
  if (!attachmentsEnabled) return null

  return (
    <button>
      Attach file {user?.firstName ? `(${user.firstName})` : ''}
    </button>
  )
}
```

## Design Notes

| Concern | Decision |
|---|---|
| Caching | None — avoids stale identity across credential rotations |
| Error handling | Falls back to anon defaults; never throws |
| Credential changes | Re-fetches automatically when `proxyEmail` (from `localStorage`) changes |
| `attachmentsEnabled` | Computed server-side; consumers read it directly without combining fields |