Appearance
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
| Action | Op | What changes | Recorded today |
|---|---|---|---|
| Revoke a Host Link | auth.hostLink.revoke | IHostLinkRecord.status: 'revoked', revokedAt, updatedAt | yes (state-file mutation) |
| Rotate Host Link credentials | auth.hostLink.credentials.refresh | heartbeatTokenHash, lastRefreshedAt, NATS user JWT replaced | yes (state-file mutation) |
| Suspend a principal | auth.principal.* (target state op; today done by editing state directly) | IHostPrincipalRecord.status: 'suspended', updatedAt | partial — state mutation visible, no audit log entry |
| Claim a namespace | auth.namespace.claim | New IHostPublicNamespaceClaim row created; Space record may be created | yes (state-file mutation) |
| Release a namespace | (target state) | claim row goes to released status | n/a |
| Transfer namespace | (target state, multi-step) | new claim by new principal after old release | n/a |
| Revoke a session | auth.session.revoke | revokedSessions[] entry added | yes (entry persists until exp) |
| Suspend a Space | (operator-only path; no public op) | IHostSpaceRecord.status: 'suspended' | partial |
| Add / remove a principal credential | auth.credential.list reads; today no public store/remove ops outside specific paths (NATS account ensure) | HostCredentialStore.credentials[] mutated | partial — state mutation visible, no audit log entry |
| Mint a NATS account credential | auth.nats.account.ensure | HostCredentialStore provider: 'nats' row added | yes (state-file mutation) |
| Add / remove a per-provider OAuth on a Device | system.factotum.credential.store / credential.delete | Device's <matrix-home>/credentials.json updated; bus event emitted | yes (bus event) |
What "recorded today" means
| Recording mechanism | What you see | What's missing |
|---|---|---|
| State-file mutation | createdAt, updatedAt, revokedAt, lastRefreshedAt on the record | Who initiated the change, why, from what session |
| Bus event | The provider that changed | The principal who changed it, the time-precise sequence with other events |
| Per-runtime log file | Process-level info | Structured 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
"Who revoked this Host Link?"
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.
Recommended logging during incident response
If you're investigating a suspected security event:
- Snapshot
system.authstate file. This is your "before" if anything mutates while you're investigating. - Subscribe to all relevant bus events going forward.
- Tail the HTTP gateway log file (
journalctl -f). - List Host Links per principal at decision points.
- 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
- Security logs — broader log story.
- Device events — bus event reference.
- Authorization / Capability tokens — how target-state authorization would create cleaner audit artifacts.
Source: state mutations identified in
projects/matrix-3/packages/system-auth/src/host-auth.ts(HostLinkStore,HostPrincipalStore,HostNamespaceStore,HostSessionService).