{
  "schema": "leumas.docs.page/1",
  "id": "pkg:@leumas/admin",
  "slug": "products/admin",
  "kind": "capabilities",
  "bucket": "product",
  "title": "Leumas Admin — @leumas/admin",
  "name": "Leumas Admin",
  "eyebrow": null,
  "chip": "@leumas/admin",
  "summary": "Leumas Admin — the CONTROL PLANE. Leumas staff only: licence issuing, EXE distribution, the customer fleet, platform trust & safety. Never ships to a customer; runs only where LEUMAS_ROLE=control.",
  "keywords": [
    "admin",
    "admin entity-kit",
    "backing service",
    "licence",
    "admin config-registry",
    "admin api",
    "leumas admin",
    "how to use admin"
  ],
  "audience": "both",
  "funnel": {
    "product": null,
    "cta": null
  },
  "body": "# Leumas Admin — `@leumas/admin`\n\n**The control plane. Leumas staff only.**\nEvery Leumas user, contact, licence, install, submission and job — one console.\n\n`admin.leumas.tech` · `LEUMAS_ROLE=control` · dev port **5100** · **never ships to a customer**.\nArchitecture: [CONTROL-PLANE.md](../../CONTROL-PLANE.md) · [APPLIANCES.md](../../APPLIANCES.md) §6\n\n---\n\n## Core functionality\n\n1. **Overview** — the pulse on one screen: MRR, active installs, DAU, pending reviews, failed\n   webhooks, incidents.\n2. **Accounts** — every Leumas user, their memberships and Stripe state, their licences. Comp a plan,\n   run a trial, replay a failed webhook, revoke a licence.\n3. **Fleet** — every Studio install: version spread, last-seen, storage, health. The opt-in studio\n   directory and the studio-to-studio relay log.\n4. **Marketplace** — the app store review desk: submissions, approve/reject, sign & publish, listings,\n   publishers and payouts.\n5. **Distribution** — releases: Studio EXE channels and rollout %, iOS release records, the\n   minimum-supported-version gate, the update feed.\n6. **Analytics** — traffic across the whole ecosystem. Full detail for Leumas's own sites; aggregates\n   for customer instances.\n7. **Trust & safety** — moderate what people publish **to** Leumas: Social, Marketplace, rooms, and\n   the accounts behind them. Contact inbox and job requests.\n\n## What it must never do\n\n| Never | Because |\n|---|---|\n| Be loaded by a customer's browser or Studio | it's staff infrastructure. Customer-facing pages live on `leumas.tech` and call `api.leumas.tech` |\n| Edit a site's content | that's a tenant-admin action → Studio. Deep-link out instead |\n| Put `three` on the EAGER graph | the domain rooms are real 3D (owner call, 2026-08-11 — this console gets full Studio parity), but the renderer stays behind `@leumas/domain-kit/room`'s lazy boundary. `pnpm check:deck` and the entry-graph check in §Verify are what keep it there |\n| Mount the Leviathan widget | `assistant={false}` in App.jsx. Its page-capability tools drive the page they are on, and the pages here revoke customer licences |\n| Grow bespoke components | four in `_shared/`, everything else from `@leumas/ui` |\n| Gate on `role === 'control'` | gate on `caps.staffPlane`, or that property quietly dies |\n\n## Security — four independent layers\n\n| Layer | Mechanism | Cost |\n|---|---|---|\n| **1 Edge** | Cloudflare Access / IP allowlist / mTLS. A non-staff request never reaches Node | infra config |\n| **2 Separate staff session** | `LEUMAS_COOKIE_NAME=leumas_staff` **and no `LEUMAS_COOKIE_DOMAIN`** → a host-only cookie under a different name. A stolen customer session is not a staff session | **two env vars** |\n| **3 Role + plane** | `isStaff = roles.includes('leumas_staff') && isControlPlane()`. Both halves required | exists |\n| **4 Grant lock** | `grantRole` refuses `leumas_staff` off-plane | exists |\n\n```bash\n# admin.leumas.tech only\n# LEUMAS_COOKIE_DOMAIN deliberately UNSET\nLEUMAS_COOKIE_NAME=leumas_staff\nLEUMAS_SESSION_TTL_HOURS=8\n```\n\nSame `Login` component, same `users` table, a cookie that exists nowhere else. **That is the \"separate\nadmin login\", and it costs no code.**\n\n## Design language\n\n- **One UI kit** — `@leumas/ui` (`st-` kit). `pnpm check:ui-kit` ratchets regrowth.\n- **One theme** — `@leumas/theme`. Never a literal near-black/near-white — `pnpm check:theme`.\n- **Command Deck home** — 7 tiles, each with a live number. The home screen *is* the status page.\n- **Cmd/K** — `navToCommands(resolveNav(VENDOR_NAV))`. Already exists, free.\n- **One table, one drawer** — every list is `VendorTable`, every row opens the same drawer. Learn it\n  once, use it seven times. The single biggest lever on \"feels light\".\n- **One customer, one page** — `CustomerCard` shows account + membership + licences + instances +\n  purchases together. Four surfaces, one component.\n- **Density over decoration** — numbers, tables, one accent colour for \"needs you\".\n\n## Composes\n\n```\n@leumas/ui  @leumas/theme        the st- kit + tokens\n@leumas/features                 RequireStaff · providers · guards · RouteTabs\n@leumas/api-client               every HTTP call\n@leumas/config-registry          defineNavManifest · resolveNav · navToCommands\n@leumas/domain-kit               the domain DOOR: routes, aside, crumbs, ⌘K, the overview HUD\n@leumas/entity-kit               the room's body/idle/approach vocabulary\n```\n\nSix packages, and the fifth is the one that matters: **this console does not own its own admin\nshell.** `@leumas/domain-kit` is the same code Leumas Studio renders, so a staff member moving between\n`admin.leumas.tech` and a customer's install is using one console with two sets of contents. If a\ncontrol-plane surface needs something the door cannot express, the fix goes in the package and Studio\ngets it too.\n\n## The shape of a domain\n\n```\n/                     Overview — the pulse deck\n/d/fleet              the DOOR: a deck of Fleet's sections over a room of live installs\n/d/fleet/overview     the tile grid, kept as a place you can go\n/fleet                FleetHub, tab '' → surfaces/fleet/Instances.jsx\n/fleet/directory      FleetHub, tab 'directory'\n/fleet/relay          FleetHub, tab 'relay'\n```\n\nFive files own a domain, and `node ops/tools/domain-scaffold/index.mjs --product admin --domain <id>`\nwrites four of them from the manifest:\n\n| File | What it is |\n|---|---|\n| `<domain>/model/tabs.js` | **THE** tab list. Pure data — the manifest, the strip, the deck and the aside all read it |\n| `<domain>/hud/overview.js` | the door's spec, pure data. `check:huds` imports it in bare Node |\n| `<domain>/hud/<domain>Populators.js` | the room's cast. **Contracted never to reject** |\n| `<domain>/hud/<Domain>Hud.jsx` | the door |\n| `<domain>/pages/<Domain>Hub.jsx` | the tab strip + the panels, which are the untouched `surfaces/` pages |\n\nThen one manifest entry (`hub(...)`) and one `LANDINGS` line in `AdminRoutes.jsx`.\n\n## File tree\n\n```\nproducts/leumas-admin/\n├── package.json [done] [warning] UNTRACKED IN GIT\n├── vite.config.js [done] port 5100 · narrow proxy (NO /leviathan /mcp /library)\n├── index.html [done]\n├── README.md [done] this file\n└── src/\n ├── main.jsx [done]\n ├── App.jsx [done] every route behind <RequireStaff> + a standing PlaneBadge\n ├── WrongDeployment.jsx [done] provider-free fallback on the wrong role\n ├── nav.manifest.js [partial] VENDOR_NAV: 4 domains → 7\n ├── AdminRoutes.jsx [todo] derived from the manifest, same pattern as Studio\n    ├── _shared/\n │ ├── VendorTable.jsx [todo] list + filter + drawer. THE table\n │ ├── CustomerCard.jsx [todo] account+membership+licences+instances. Used by 4 surfaces\n │ ├── ReviewPanel.jsx [todo] approve/reject + reason. Submissions and Trust\n │ └── useVendor.js [todo] the 3 fetch hooks (list / detail / act)\n    └── surfaces/\n ├── Overview.jsx [todo] the pulse deck\n        ├── accounts/\n │ ├── Directory.jsx [partial] from Accounts.jsx\n │ ├── Memberships.jsx [todo] comp · trial · replay a failed webhook\n │ └── Licences.jsx [partial] from IssuedLicences.jsx\n        ├── fleet/\n │ ├── Instances.jsx [todo] version · last-seen · storage · health\n │ ├── Directory.jsx [todo] opt-in studio directory\n │ └── Relay.jsx [todo] studio<->studio envelope log + rate limits\n        ├── marketplace/\n │ ├── Submissions.jsx [todo] the review queue\n │ ├── Listings.jsx [todo] published apps · unpublish · pricing\n │ └── Payouts.jsx [todo] publisher earnings\n        ├── distribution/\n │ ├── Releases.jsx [done] Studio EXE builds\n │ └── Channels.jsx [todo] stable/beta · rollout % · min iOS version\n ├── Analytics.jsx [todo] ecosystem · sites · instances\n └── TrustSafety.jsx [done] moderation queue\n```\n\n**~20 files, 4 shared components.** That's the whole console.\n\n## Backing service\n\n```\nshared/services/control/ [todo] @leumas/control — mounted at /control when caps.staffPlane\n└── src/\n    ├── router.js                   ALL behind requireStaff\n    ├── overview.js                 the pulse aggregation (one query set, cached 30s)\n    ├── fleet.js                    instances · opt-in directory · relay log\n    ├── analytics.js                ecosystem + rollup reads\n    └── moderation.js               thin — delegates to router-kit's social-moderation\n```\n\n## Roadmap\n\n### [todo] V0 · Commit it\n\n- Status: todo — **`git add products/leumas-admin`** — every file is `??` today. A `git clean` deletes it\n\n### [todo] V1 · Deploy the plane (roadmap A2)\n\n- Status: todo — `admin.leumas.tech` at `LEUMAS_ROLE=control` + this SPA\n- Status: todo — The two cookie env vars above\n- Status: todo — Edge restriction: Cloudflare Access / IP allowlist / mTLS\n\n### [todo] V2 · The 7-domain shell (roadmap A4)\n\n- Status: todo — `VENDOR_NAV` → 7 domains; `AdminRoutes.jsx`; the four `_shared/` components\n- Status: todo — Reorganize surfaces into `accounts/` `fleet/` `marketplace/` `distribution/`\n- Status: todo — `Overview.jsx` with live numbers\n- Status: todo — [warning] Route names must not collide with API namespaces: **`/licences` not `/licensing`**,\n      **`/market` not `/marketplace`** — the dev proxy forwards those, so a navigation gets the API's 404\n- Status: todo — `@leumas/control` service\n\n### [todo] V3 · Fleet (roadmap A3)\n\n- Status: todo — `license_activations` += `tenantId` · `report` · `lastReportAt` · `mailboxCursor` · `listed`\n- Status: todo — `Instances.jsx` — version spread, last-seen, storage, per-tenant\n- Status: todo — [warning] Label it honestly: the fleet only ever sees **licensed** installs. A dev checkout or an\n      install with no `LEUMAS_LICENSE_SERVER` has no central row at all\n\n### [todo] V4 · Marketplace review (roadmap C1, C3)\n\n- Status: todo — `Submissions.jsx` — manifest validity, permissions vs description, bundle hash, origin ownership\n- Status: todo — Approve → **sign** (Ed25519, reuse `@leumas/licensing`) → publish → **write the PassNode rule\n      from the manifest's declared `pricing`**\n- Status: todo — `Listings.jsx`, `Payouts.jsx`\n\n### [todo] V5 · Distribution + Analytics (roadmap D3)\n\n- Status: todo — `Channels.jsx` — stable/beta, rollout %, per-customer channel pinning\n- Status: todo — iOS release records + the minimum-supported-version gate\n- Status: todo — `Analytics.jsx` — **design for the aggregate tier**; let Leumas's own sites be the richer special\n      case, or every chart breaks when a third-party instance appears\n\n### [todo] V6 · Support\n\n- Status: todo — Contact inbox for leumas.tech + job requests\n- Status: todo — Escalations from the Trust queue\n\n## Run\n\n```bash\npnpm dev:admin\n```\n\nPort 5100. Requires a `control` or `dev` role API — `WrongDeployment.jsx` catches the mistake.\n\n## Verify\n\n```bash\npnpm check:huds        # both consoles' doors: spec, shell, populator, no renderer in a console\npnpm check:nav         # every route in exactly one domain, in all three manifests\npnpm check:admin       # every surface goes through AdminSurface; the CSS/inline ratchets\npnpm --filter @leumas/admin build\n```\n\nThen prove the renderer is still lazy — the one thing a browser will not show you:\n\n```bash\ngrep -c WebGLRenderer products/leumas-admin/dist/assets/index-*.js   # must be 0\nls products/leumas-admin/dist/assets/three.module-*.js               # its own chunk, unreferenced by the entry\n```\n",
  "source": {
    "path": "products/leumas-admin/README.md",
    "blobSha": "",
    "commit": "",
    "committedAt": "",
    "provenance": "no-git",
    "bytes": 12167,
    "hash": "280b2d46d3b8d42e74d92c111bb21dacc2b082e5"
  },
  "urls": {
    "html": "/p/products/admin",
    "json": "/docs/products/admin.json",
    "md": "/docs/products/admin.md"
  },
  "links": {
    "composes": [
      "pkg:@leumas/apps",
      "pkg:@leumas/config-registry",
      "pkg:@leumas/domain-kit",
      "pkg:@leumas/entrance",
      "pkg:@leumas/features",
      "pkg:@leumas/schemas",
      "pkg:@leumas/site-map",
      "pkg:@leumas/sound",
      "pkg:@leumas/theme",
      "pkg:@leumas/ui",
      "pkg:@leumas/value-changer"
    ],
    "usedBy": [],
    "product": [],
    "howTo": [],
    "skills": [
      "skill:leumas-domains"
    ]
  },
  "exports": null
}
