Appearance
Dashboard: Spaces
Status: target state. No cross-principal Space list UI exists. The data is in
<host-home>/state/auth-state.jsonand accessible per-principal viasystem.authops.
Target view
A platform-wide Space list with per-row:
spaceId,spacePath,publicNamespace,authorityRoot.- Owner (
ownerType+ownerPrincipalIdorownerOrganizationId). - Status (
active/suspended). - Created at, updated at.
- Linked Device count (joined from
system.devices). - Public namespace claim type and verification level.
Filterable by:
- Status.
- Owner type.
- Verification level.
- Created-at range.
- Free-text on
spacePath.
Per-row actions:
- Inspect (deep view: claims, Host Links, runtimes).
- Suspend / unsuspend (target).
- Transfer ownership (target).
Today's data
Per-principal listing
bash
matrix invoke system.auth auth.namespace.list '{"principalId":"p_xxx"}'Returns the namespace claims for one principal, each carrying spaceId, spacePath/routeKey, publicNamespace, canonical, status. Cross-reference with auth.space.get for the full Space record.
Cross-principal listing (no op today)
There is no auth.space.list op. To enumerate every Space:
bash
# Read the state file directly (platform Host)
sudo jq '.spaces' <host-home>/state/auth-state.json
sudo jq '.publicNamespaces' <host-home>/state/auth-state.json
sudo jq '.principalDefaultSpaces' <host-home>/state/auth-state.jsonEach spaces[] row matches the IHostSpaceRecord schema. Each publicNamespaces[] row is an IHostPublicNamespaceClaim.
For programmatic use, write a script that joins these arrays:
ts
const state = JSON.parse(fs.readFileSync(path));
const spacesByOwner = new Map();
for (const space of state.spaces) {
const list = spacesByOwner.get(space.ownerPrincipalId) ?? [];
list.push(space);
spacesByOwner.set(space.ownerPrincipalId, list);
}What the target requires
To implement the dashboard:
auth.space.listop with optional filters (status, owner type, owner id, page cursor).auth.space.suspend/unsuspendops to wire up actions.- A join with
system.devicesto compute linked Device count per Space. - A UI in
matrix-webrendering the table + actions.
None of those steps is large; the admin dashboard is gated more by prioritization than by complexity.
Schema recap
ts
interface IHostSpaceRecord {
id: string; // spaceId
authorityRoot: string;
ownerPrincipalId?: string;
ownerType?: 'principal' | 'organization' | 'system' | 'service';
status: 'active' | 'suspended';
createdAt: string;
updatedAt: string;
}The schema does not carry displayName for a Space — the user-facing label is the spacePath from the canonical namespace claim. Spaces without a public namespace claim (rare) would need synthesized display logic.
Edge cases for the target UI
- Spaces with no public namespace claim. Display by
spaceIdor(deferred). - Spaces owned by
ownerType: 'organization'when no organization records exist (today). Show as(unowned org). - Suspended Spaces. Highlight visually.
- Spaces whose
authorityRootdoes not match any principal's address roots (legacy data). Flag.
Operator workaround for common tasks
"How many Spaces does this principal have?"
bash
matrix invoke system.auth auth.namespace.list '{"principalId":"p_xxx"}'
# Count distinct spaceId values."Who owns this Space?"
bash
matrix invoke system.auth auth.space.get '{"spaceId":"spc_xxx"}'
# Returns ownerPrincipalId, then auth.principal.get on it."Suspend this Space"
Hand-edit auth-state.json (under hivecast stop to avoid concurrent writes), set status: 'suspended', restart. Until auth.space.suspend ships.
See also
- Overview / Spaces — concept.
- Accounts and Spaces / Spaces — operator workflow today.
- Reference: Admin actor ops — full op list.
- Operations: Pair a device — admin pair flow.
Source:
projects/matrix-3/packages/system-auth/src/host-auth.ts:61-85for the schema.index.ts:655-717for current ops.