content
# R002 — Admin token is publicly readable. Rotation currently provides no benefit.
**Raised by:** CC [100], 2026-08-21
**Severity:** Critical — credential exposure leading to remote code execution
**For:** Master [10], Server [40]
**Handling:** Do NOT store this file under `systems/` or any other path that returns 200 to
anonymous requests. Confirmed public: `systems/10-master/gov/handoff-10.md`,
`systems/governance/SOP-INDEX.md`. Use a 403'd location or keep it off the server.
---
## The finding
The platform admin token is hardcoded in plain text in seven browser-delivered files, all of
which are served to anonymous requests with no token, cookie, referer or login:
| File | HTTP (anonymous) |
|---|---|
| `backend/sys-com/dashboard.html` | 200 — token in source |
| `backend/sys-com/cmd-popup.js` | 200 — token in source |
| `backend/sys-com/panel.html` | 200 — token in source |
| `backend/sys-com/commands.html` | 200 — token in source |
| `backend/tools/db-admin.html` | 200 — token in source |
| `backend/web/media-viewer.html` | 200 — token in source |
| `backend/jobs/index.html` | 200 — token in source |
Verified 08/21/26 by anonymous `curl` with no credentials of any kind. Anyone who opens the
dashboard URL and views source has the admin token.
## Why this is critical rather than merely bad
The token is not a read credential. `file_write_web.php` accepts a token plus an **arbitrary
path** and **arbitrary content**, anywhere under `public_html`, **including `.php`**. Token
therefore equals the ability to write and execute code on the server.
Chain: public URL → view source → admin token → arbitrary file write → remote code execution.
This is the second half of R001. R001 established that the token was sprawled across ~953
local and ~138 server files. What was not established until now is that **seven of those
files are publicly served.**
## Rotation is currently a no-op
Phase 4 retired the old token on 08/21/26. That immediately broke all eleven systems, because
their EXEC_OPEN boot documents still carried it (see B006). The old token was then brought
back out of retirement the same evening, so **both tokens are live right now**.
The new token was written into the same publicly-readable files. It is therefore as exposed as
the old one, from the moment it was created.
**Net result of the rotation: one outage, five degraded sessions, no reduction in exposure.**
Rotation exists to shrink the window in which a stolen credential is useful. If the new
credential is published on creation, that window is zero. Changing the locks and taping the
key to the door.
**Recommendation: do not rotate again until delivery is fixed.** Each further rotation costs
an outage and buys nothing.
## Related defect found in the same pass
The rotation edit introduced a doubled prefix — `yttcom-admin-yttcom-admin-` — in four files:
`dashboard.html`, `panel.html`, `commands.html`, `cmd-popup.js`. Every API call from those
pages returns Unauthorized, which is why the dashboard loads but renders empty. A bad
find-and-replace where the replacement string already contained the prefix.
Patched copies staged at `claudecode/platform/patches/`. Each lost exactly 13 bytes per
occurrence and nothing else changed. Not yet deployed — see B007.
Also, three files were **missed** by the rotation entirely and still carry only the old token:
`backend/web/web-fetch.html`, `backend/web/media-viewer.html`, `backend/jobs/index.html`.
They work only because the old token is live again, and break the moment it is retired.
## What is correctly protected
`.htaccess` is doing real work and this is not a total exposure:
- `backend/config/platform.php` — 403
- `backend/records/db/records.db` — 403
- EXEC_OPEN files — 403
- `backend/tools/file-editor.php` — 401
The problem is specific: **admin credentials were placed in files that a browser must be able
to read.** No server-side rule can protect those, because the browser needs the contents in
order to run them.
Two lesser issues in the same area: `backend/sys-com/` returns a **directory listing** (200,
7440 bytes), and gov files under `systems/` are world-readable — no secrets in them, but the
platform's internal structure, jurisdictions and handoffs are public.
---
## The fix
**Principle: a secret must never be delivered to a browser.** Not obfuscated, not minified,
not encoded. Anything the browser can read is public by definition.
### Recommended — proxy endpoint, smallest correct change
Two pieces already exist, which makes this cheaper than it sounds:
1. `backend/config/platform.php` already returns 403 — a working place to store a secret
2. The pages already call same-origin PHP
So:
- The page calls its own server with **no credential**: `proxy.php?action=get_sync_status`
- `proxy.php` checks the caller is logged in, then reads the token from config **server-side**
and makes the real call
- The dashboard's `const TK = '...'` line is **deleted**, not replaced
Add a session gate in front of it — log in once, server sets an HttpOnly cookie holding a
meaningless session ID, JavaScript cannot read it. This is what cPanel, WordPress and Grafana
all do.
### Machine callers are a different case
The AI sessions are not browsers, and a token is genuinely correct for them. But:
- It must be a **separate token** from anything a browser touches, so one leak does not hand
over the other
- It should travel in an **`Authorization` header, not a `?token=` query string**
**Query strings get logged.** The token currently appears in server access logs, browser
history, proxy logs, and referer headers on every single call. Fixing the public-file problem
still leaves the credential written into logs nobody will think to clean.
### Reduce blast radius regardless
Split read from write. A read-only credential for display; the write credential never leaves
the server. Then a leak costs data exposure rather than code execution.
---
## Suggested order
1. **Deploy the four doubled-prefix fixes** — restores the dashboard. B007.
2. **Turn off the directory listing** on `backend/sys-com/`. One line.
3. **Build the proxy + session gate.** Server [40]'s jurisdiction. This is the actual fix.
4. **Move machine callers to an `Authorization` header** with a separate token.
5. **Only then rotate**, and migrate the eleven EXEC_OPENs *before* retiring anything —
that ordering error is what caused the 08/21 outage.
6. **Consider whether gov files should be world-readable.** Separate decision, no secrets at
stake, but the platform's internals are currently public.
Steps 1 and 2 are tonight. Step 3 is the real work and belongs to Server [40].