# Caching Status "Not Implemented" Fix Plan

## Root Cause

The caching page shows "Service status unavailable (caching_status command not
implemented on relay)" — but the command **is** implemented at
[`src/config.c:4128`](../src/config.c:4128). The real problem is **admin
authorization failure**: every admin command from the browser is rejected at
publish time with:

```
Unauthorized admin event attempt: invalid admin pubkey
```

This rejection happens at [`src/main.c:2249`](../src/main.c:2249) because the
browser's pubkey is not in the relay's `admin_pubkey` config list. The kind
23456 event never reaches the command handler, so `caching_status` never
executes, and the placeholder text persists.

### Confirmed admin key

- Relay admin pubkey (hex): `6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb3`
- Relay admin npub: `npub13lm5wf8dvsdnc2894pkhch9uf8phvw9varrv8zf4sc885hhdmc8q6lx7ks`
- Source: [`.relay.laantungir.net.keys`](../.relay.laantungir.net.keys:1)

The browser extension (nos2x) was using pubkey `8ff74724...`, which is not an
admin on the port 7777 relay.

## Secondary Issue (latent)

Even after auth is fixed, the caching status UI would render **empty blocks**
due to a schema mismatch between backend and frontend:

| Frontend expects ([`api/index.js:7046`](../api/index.js:7046)) | Backend emits ([`src/config.c:4135`](../src/config.c:4135)) |
|---|---|
| `data.service.enabled` | `data.caching_enabled` |
| `data.service.running` | *(not emitted)* |
| `data.inbox.enabled` | `data.caching_inbox_enabled` |
| `data.inbox.queue_depth` | `data.inbox_pending_live` + `data.inbox_pending_backfill` |
| *(not expected)* | `data.inbox_total_dequeued`, `data.inbox_total_accepted`, `data.inbox_total_rejected`, `data.inbox_oldest_age_seconds` |

## Fix Steps

```mermaid
flowchart TD
    A[Step 1: Fix admin auth] --> B[Step 2: Align response schema]
    B --> C[Step 3: Fix frontend handler]
    C --> D[Step 4: Improve error messaging]
    D --> E[Step 5: Test end-to-end]
```

### Step 1 — Fix admin authorization (config, user action)

Load the admin private key (corresponding to `6a04ab98...` /
`npub13lm5wf8dvsdnc2894pkhch9uf8phvw9varrv8zf4sc885hhdmc8q6lx7ks`) into the
nos2x browser extension so admin commands authenticate. Use `nak` on the
command line to convert/derive the nsec if needed.

This unblocks **all** admin commands, not just caching.

### Step 2 — Align backend response schema (code, [`src/config.c:4128`](../src/config.c:4128))

Restructure the `data` object in the `caching_status` handler to emit nested
`service` and `inbox` objects matching the frontend handler:

```json
{
  "command": "caching_status",
  "status": "success",
  "data": {
    "service": {
      "enabled": false,
      "running": false,
      "connected_relays": 0,
      "events_cached": 0
    },
    "inbox": {
      "enabled": false,
      "running": true,
      "queue_depth": 0,
      "last_poll": 0,
      "total_dequeued": 0,
      "total_accepted": 0,
      "total_rejected": 0,
      "total_duplicates": 0,
      "pending_live": 0,
      "pending_backfill": 0,
      "oldest_age_seconds": 0
    }
  }
}
```

Populate `service.running` / `connected_relays` / `events_cached` from the
caching service launcher state if available; otherwise emit zeros/defaults.

### Step 3 — Update frontend handler (code, [`api/index.js:7027`](../api/index.js:7027))

Update `handleCachingStatusResponse()` to render the inbox poller stats the
backend actually produces (dequeued/accepted/rejected/pending/oldest_age), and
map `data.inbox.pending_live + pending_backfill` to queue depth.

### Step 4 — Replace misleading placeholder (code, [`api/index.js:6889`](../api/index.js:6889))

- Change the pre-send placeholder from "caching_status command not implemented"
  to a neutral "Loading..." message.
- On auth failure, show the actual error (e.g., "Not authorized: pubkey not
  registered as admin") instead of the generic "unavailable" text.

### Step 5 — Test end-to-end on port 7777

Verify the caching page shows real service/inbox status after auth is fixed
and the schema is aligned.
