← all documents · raw markdown · 11 KB

ORACLE OS — Role-Separation Goal (Track F)

Doc type: Single source-of-truth plan for splitting the Oracle OS Command Center into

an owner/admin Command Center and a member/viewer public portal, served from the

same oracle.html + command-center-server.mjs on :3001.

Owner: Ryan (nastermodx) — tester/verifier.

Reconstructed: 2026-07-01 from the live code (the original detailed plan was overwritten

and is not in git; only the Track F summary in ORACLE-OS-COMMAND-CENTER-GOAL.md §5 survived).

This doc replaces that partial stub and is grounded in the ACTUAL auth/role system in the

code, not memory.

---

1. The concept (what we are building)

Serve two audiences from the one dashboard, with role decided server-side and

enforced on both sides:

role:'admin' minted user such as Taz): the FULL Command Center — every control,

fleet restart / sleep / wake, provider & connection internals, hardware/CPU/RAM/GPU/disk

telemetry, raw logs, engine tests, sensitive channels, the admin panel, user minting —

everything that exists today.

read-mostly subset (public channels + chat, agent status/leaderboard, the full KAIVERSE,

and any sanitized public surface), with all admin/dangerous controls and sensitive

data HIDDEN client-side AND withheld server-side. A member must not be able to see

hardware stats or trigger control actions even by calling the APIs directly.

Security principle: CSS/JS hiding is UX, not security. Every gate is enforced twice

declaratively in the client (data-needs="admin" + body.scope-member) *and* authoritatively

in the server (role-checked endpoints + role-filtered response bodies). The client is never

trusted to assert its own role; the server resolves role from the session/token.

---

2. Current state of the role system (what EXISTS vs what's MISSING)

Audited against command-center-server.mjs and oracle.html on 2026-07-01.

2.1 Server — the identity/role foundation is ALREADY BUILT and solid

ROLE_RANK, and ROLE_DEFAULT_PERMS (owner adds user_management, admin adds control+discord,

member gets read+own_data, viewer gets read).

(seedOwner() ~L502) as usr_owner so the legacy owner login is unchanged.

x-cc-token/?token=resolveUserByToken(), OR the legacy owner token → usr_owner.

When auth is disabled (no CC_CONTROL_TOKEN) it returns the owner so nothing breaks.

return null when the caller is missing/under-ranked. requireControl = admin+,

requireOwner = owner.

authenticated session — so the client CAN reliably learn its role.

/api/control/restart, /api/control/ai/<name> (sleep/wake/restart),

/api/ai/<name>/config, channel-settings write, /api/training-send,

/api/channel-send, POST /api/owner-profile. User management (/api/users*) is

owner-only via requireOwner.

so only owner/admin unlock the admin/secret channel IDs; member/viewer get read scope.

2.2 Client — the gating hooks EXIST but the master switch is NEVER FLIPPED

(L2122–2129) and the owner-only buttons in renderAdmin() (L4318–4319).

(~L2505) exist and already gate a few inline controls (AI config alert L5312, roster

Controls button L5691, user-mgmt render L4334).

ONLY in the CSS rule (L514) and there is no classList.add('scope-member') anywhere. So

the entire data-needs="admin" mechanism is dormant — a logged-in member currently

sees the admin controls in the DOM. The switch is built but never thrown.

2.3 Server — the read-side leaks that are NOT yet role-filtered

user — CPU model/percent/per-core, RAM used/total, GPU, disk, host name/platform/release,

uptime, engine process RSS, provider internals + cooldowns. This is exactly the

hardware/CPU/RAM leakage Track F forbids for members. No role filter.

readable by any authenticated user. No role gate.

readable by any authenticated user. No role gate.

is scoped, but message-content endpoints do not re-check whether the requested channel is an

admin/secret channel. A member could request an admin channel's transcript directly. (Deeper

fix — flagged for Phase 1.5/2, see §4.)

2.4 Client — admin surfaces lacking data-needs

(acc-logs, L2150) have no data-needs="admin" — members would see them (and they poll the

leaky endpoints above).

~L4620+) rather than static tagged containers, so hiding them cleanly is a Phase 2 layout job.

---

3. Requirement (the contract)

1. Role is decided server-side from session/token and exposed via /api/me. Client mirrors

it but never asserts it.

2. Client: for any non-admin session, body.scope-member is set so every data-needs="admin"

element is hidden; default to the hidden (least-privilege) state until /api/me resolves so a

slow load never flashes controls.

3. Every admin-only control/panel carries data-needs="admin" (or an equivalent role check):

fleet/server restart, per-AI sleep/wake/restart + config, Server Control, Test Requests, Logs,

provider/connection internals, hardware telemetry, RF/camera admin bits, voice-call admin

actions, user management.

4. Server: every sensitive action endpoint rejects non-admin sessions with 403

(already true for the control endpoints; extend to any that trust the client). Every sensitive

data endpoint returns a role-filtered body — members get a sanitized subset, never

hardware/provider internals or raw logs/tests.

5. Never weaken the owner. The CC_CONTROL_TOKEN holder / usr_owner / any admin keeps

FULL access. Restrictions are ADD-ONLY for member/viewer. No change may lock the owner out.

---

4. Phased plan

Phase 1 — make the existing owner-vs-member gating correct & complete (client + server) — SAFE, FOUNDATIONAL

The part that clearly cannot lock out the owner. This is the phase implemented now.

1. Throw the master switch: default <body class="scope-member"> (least-privilege) and, in

loadMe(), remove scope-member only when isAdminRole() is true; re-add it otherwise.

Owner/admin never carry the class → never hidden. This activates every existing

data-needs="admin" rule.

2. Add data-needs="admin" to the ungated admin accordions: Test Requests (acc-tests)

and Logs / Issues (acc-logs).

3. Role-filter /api/system-stats: admin+ gets the full body; member/viewer get a sanitized

subset (agent counts + minimal bot roster: name/online/role/color) with no cpu/ram/gpu/

disk/host/uptime/engine/procRss/providers/cooldowns.

4. Gate /api/logs and /api/tests with requireControl (admin+) → members get 403; the

now-hidden client panels never call them anyway.

owner/admin → full data + no 403. Restrictions are purely additive for non-admins.

Phase 1.5 — close the remaining server read-leaks (needs light live verification)

if the requested channel ∈ ADMIN_CHANNEL_IDS and the caller is not admin+, return an empty/403

scoped result. (Deferred out of Phase 1 because it touches the transcript read path and wants a

live test to confirm public channels still load for members.)

Phase 2 — the member public-portal LAYOUT

hardware + provider/connection cards in renderHome(), relabel the "Admin" identity accordion

as a plain profile card, and surface the member-appropriate set (public channels + chat, agent

leaderboard/status, full KAIVERSE, any sanitized public vitals). Purely additive, role-branched

rendering.

Phase 3 — per-role features & polish

no chat compose) vs member (chat allowed) differences, lock/resume UX, and data-center-grade

hardening. Optional Discord-OAuth verification of the currently owner-asserted discordId.

---

5. Apply / test

leaves the engine, pipeline, and bots alone).

log in as that user in a second browser → confirm no hardware stats, no control buttons,

/api/system-stats sanitized, /api/logs & /api/tests → 403, control POSTs → 403.

---

*Grounding: the live command-center-server.mjs (roles/registry L441–608, endpoint gates

L3369–4332) and oracle.html (scope CSS L514, data-needs L2122+, loadMe L6125) as of 2026-07-01,

plus ORACLE-OS-COMMAND-CENTER-GOAL.md §5 Track F.*