Skip to content

Admin actions

This page enumerates the operator-level actions that materially change security state, and tells you where each one is recorded today (and where it isn't).

The action list

ActionOpWhat changesRecorded today
Revoke a Host Linkauth.hostLink.revokeIHostLinkRecord.status: 'revoked', revokedAt, updatedAtyes (state-file mutation)
Rotate Host Link credentialsauth.hostLink.credentials.refreshheartbeatTokenHash, lastRefreshedAt, NATS user JWT replacedyes (state-file mutation)
Suspend a principalauth.principal.* (target state op; today done by editing state directly)IHostPrincipalRecord.status: 'suspended', updatedAtpartial — state mutation visible, no audit log entry
Claim a namespaceauth.namespace.claimNew IHostPublicNamespaceClaim row created; Space record may be createdyes (state-file mutation)
Release a namespace(target state)claim row goes to released statusn/a
Transfer namespace(target state, multi-step)new claim by new principal after old releasen/a
Revoke a sessionauth.session.revokerevokedSessions[] entry addedyes (entry persists until exp)
Suspend a Space(operator-only path; no public op)IHostSpaceRecord.status: 'suspended'partial
Add / remove a principal credentialauth.credential.list reads; today no public store/remove ops outside specific paths (NATS account ensure)HostCredentialStore.credentials[] mutatedpartial — state mutation visible, no audit log entry
Mint a NATS account credentialauth.nats.account.ensureHostCredentialStore provider: 'nats' row addedyes (state-file mutation)
Add / remove a per-provider OAuth on a Devicesystem.factotum.credential.store / credential.deleteDevice's <matrix-home>/credentials.json updated; bus event emittedyes (bus event)

What "recorded today" means

Recording mechanismWhat you seeWhat's missing
State-file mutationcreatedAt, updatedAt, revokedAt, lastRefreshedAt on the recordWho initiated the change, why, from what session
Bus eventThe provider that changedThe principal who changed it, the time-precise sequence with other events
Per-runtime log fileProcess-level infoStructured fields for filtering

The fields createdAt etc. tell you "what" and "when" but not "who" or "why". Today, "who" is implicit (the only operator who could call this op was the principal whose session was active), but it's not recorded against the record.

Target state — admin audit log

A unified admin-audit log would record per action:

ts
interface IAdminAuditEntry {
  timestamp: string;
  action: 'auth.hostLink.revoke' | 'auth.namespace.claim' | ...;
  initiatedBy: { principalId: string, sessionJti: string, ipAddress?: string };
  target: { principalId?: string, hostLinkId?: string, namespace?: string, ... };
  before?: <relevant snapshot>;
  after?: <relevant snapshot>;
  reason?: string;        // optional operator-supplied reason
  outcome: 'ok' | 'rejected' | 'failed';
  failureError?: string;
}

This would close the "who, why" gap. Implementation cost: a system.audit.admin.append op called from the gateway on every state-changing call, plus storage retention policy.

Operator runbooks against current state

Today: not directly answerable. The state record carries revokedAt but not revokedByPrincipalId. Inspect HTTP gateway log files for the revocation request:

bash
sudo journalctl -u hivecast-host.service --since "1 day ago" \
  | grep -E "auth.hostLink.revoke"

Cross-reference with the cookie / Bearer token in that request to identify the session, then revokedSessions[] (if revoked) or principal store (otherwise) to identify the principal.

Target state: the record has revokedByPrincipalId.

"Did anyone fail to authenticate in the last hour?"

Today: HTTP gateway logs show 401/403. There's no aggregated query.

bash
sudo journalctl -u hivecast-host.service --since "1 hour ago" \
  | grep -E "(401|403)"

"What namespaces does this principal own?"

Today: auth.namespace.list (or auth.namespace.resolve per namespace).

bash
matrix invoke system.auth auth.namespace.list \
  '{"principalId":"p_<sha20>"}'

The returned IHostPublicNamespaceClaim[] includes createdAt for each. No "added by" field today; "added by" is implicit (ownerPrincipalId === principalId).

"Did anyone change auth.sessionSecret recently?"

Today: not tracked by the system. Operator config-management discipline (git, deploy logs, ChatOps) is responsible for recording this.

If you're investigating a suspected security event:

  1. Snapshot system.auth state file. This is your "before" if anything mutates while you're investigating.
  2. Subscribe to all relevant bus events going forward.
  3. Tail the HTTP gateway log file (journalctl -f).
  4. List Host Links per principal at decision points.
  5. Document each action you take (Slack/wiki) so the audit log can be reconstructed manually.

Once unified audit lands, this manual ceremony is replaced by the system.audit actor.

See also

Source: state mutations identified in projects/matrix-3/packages/system-auth/src/host-auth.ts (HostLinkStore, HostPrincipalStore, HostNamespaceStore, HostSessionService).