operator-admin-isis-provenance.mdoperator-admin.mdoperator-audit.mdoperator-tenant.md04-auth-session.mdJourney flow#
Generated from the authored steps below — click a node to jump to that section.
/operator/audit combines two distinct read surfaces: a compact server-rendered
ledger from /v1/audit, and an interactive explorer over the shared admin audit
event store. The explorer can filter, save/load an operator-owned query, export
JSON or Markdown, and delete it. The BFF additionally supports description,
bookmark, patch, and share-token operations, but those controls are not in the
UI—and the issued share token has no read/consume route.
Personas#
- Platform operator — holds
admin:*and searches events across operator workspaces. - Incident or tenant investigator — narrows a prefix, actor, workspace, text needle, and time interval.
- Compliance reviewer — downloads a reproducible evidence bundle and needs to know whether integrity and retention claims are actually enforced.
- Second operator — illustrates the boundary between a token being issued and a genuinely shareable investigation.
Pre-conditions#
- The operator is signed in and the page-level operator gate allows
/operator/audit. - Interactive
/v1/admin/audit-log/*access requires exactlyadmin:*inadmin-audit-log.ts.admin:studioand tenant-console workspace scopes do not pass this current gate, although the explorer's error copy still namesadmin:studio. - The admin audit-events store may be durable on configured Postgres; without the admin database, events/investigations are process-local.
- Saved investigations are bucketed by authenticated operator user id, not by tenant. This is a platform-wide explorer, not a tenant-admin projection.
Steps#
1. Open the two-layer audit page#
The upper ledger server-loads /v1/audit and renders When, Actor, Action, and
Target newest-first. That endpoint is a separate compact audit contract from the
explorer below; matching rows between the two stores is not guaranteed.
/operator/admin and /operator/incidents provide links to /operator/audit.
Anonymous navigation is redirected before either layer renders.
2. Search the live admin event store#
OperatorAuditExplorer calls GET /v1/admin/audit-log/events with optional
needle, one event-type prefix, comma-separated actor/workspace ids, from/to
Unix seconds, and a UI-fixed limit of 25. The store performs case-insensitive
needle matching over title, subtitle, event type, and workspace and returns
newest-first events plus the normalized filter.
The prefix is free text rather than a typed taxonomy picker. Useful live
families include audit.investigation.*, admin.integrations.*,
admin.bulk_*, incident.*, tenant.scim_provision.*, and
tenant.oneroster.*.
3. Save and reload a query#
The shipped UI accepts only an investigation title and the current filter.
POST /v1/admin/audit-log/investigations creates an operator-owned record; the
saved list can reload that filter and re-run the search. A maximum of 100
records per operator is enforced by the store.
The API also accepts an optional 2,000-character description and up to 200
bookmarked event ids, and PATCH can update title, description, filter, or
bookmarks. No description editor, event bookmark button, or PATCH action is
rendered in OperatorAuditExplorer.
4. Export JSON or Markdown#
Export re-runs the saved filter with a 500-event limit and resolves bookmarked
ids against the current event store. Missing bookmarks are silently omitted.
JSON contains the investigation, matched events, surviving bookmarks, and
generatedAt; Markdown contains the title, description, created/updated times,
optional bookmarks, and matched rows.
The browser receives the payload and creates a local Blob download. No export artifact is stored server-side. Neither format includes a manifest hash or signature, and the route does not enforce a retention boundary.
5. Exercise the API-only patch and token seams#
Real HTTP coverage patches notes/bookmarks and posts /:investigationId/share.
Share returns a raw token once and stores its SHA-256 hash with the
investigation. There is no endpoint that accepts the token to read an
investigation, no shared-link UI, and no cross-operator authorization flow. The
current feature is token issuance, not working collaboration.
6. Delete and inspect the meta-audit trail#
Delete removes the operator-owned investigation and its stored token hash.
Successful create, update, share, export, and delete operations append
audit.investigation.<action> rows with filter/bookmark metadata; raw share
tokens are excluded. Invalid create and duplicate delete do not mint phantom
events.
Post-conditions#
- The operator can search the live admin event store and save/reload one named query from the browser.
- JSON and Markdown downloads reflect a fresh export-time query, not a frozen event snapshot.
- Investigation lifecycle actions are themselves auditable and the raw share token is not placed in audit payloads.
- Description, bookmark, patch, and share-token creation work at the API layer only.
- Cross-operator share consumption, tamper-evident export, tenant-admin isolation, redaction, concurrency control, and retention indication are not shipped.
Failure modes#
- Scope-copy mismatch — the server accepts only
admin:*, while explorer denial copy saysadmin:* or admin:studio. - Two unrelated ledgers — the upper
/v1/audittable can be green while the lower admin event store is empty or unavailable. - No tenant boundary —
workspaceIdInis an optional filter, not an authorization constraint; this route is platform-operator only. - Share token dead end — a valid issued token cannot open anything because no consumer route exists.
- Bookmark disappearance — deleted/missing events vanish silently from an export; the UI reports no missing count.
- No chain of custody — downloads have no canonical digest, signature, or verification command.
- Last-write-wins PATCH — there is no ETag/version precondition for concurrent updates.
- Retention ambiguity — old rows can disappear from the event store without a query warning; downloaded Blobs are outside server retention control.
- Durability configuration — without the admin database, saved investigations and event history do not survive restart.
E2E coverage#
apps/oshun/web/e2e/tenant-audit-log-investigation.spec.tscovers the upper ledger, anonymous gate and entry links; real-BFF 401/403, event filtering, create/list/PATCH/share/export/delete and meta-audit read-back; plus UI filtering, save/load, JSON/Markdown Blob export, and delete.- Coverage depth: deep for shipped/API seams, partial for the named collaboration outcome. Bookmark/share are API-only, and no test can prove token consumption, cryptographic export verification, redaction, or tenant-scoped access because those contracts are absent.
Per-view files touched#
operator/operator-audit.md— static ledger plus interactive explorer.operator/operator-admin.md— audit entry and downstream operator actions.operator/operator-tenant.md— tenant context used as an optional workspace filter.operator/operator-admin-isis-provenance.md— provenance/drift follow-up.shell/04-auth-session.md— operator role and scope gate.
Cross-references#
incident-triage.md— incident events searched here.tenant-bulk-member-invite-scim.md— tenant/SCIM audit producers.tenant-integrations-api-keys-webhooks.md— integrations event producers.- Sources:
apps/oshun/web/src/app/operator/audit/page.tsx,apps/oshun/web/src/components/lilith/OperatorAuditExplorer.tsx,apps/oshun/bff/src/routes/admin-audit-log.ts, and the twoadmin-audit-*-store.tsfiles.
Open questions#
- Is this intentionally platform-only, or what tenant-scoped role and enforced workspace predicate should a tenant admin receive?
- Should the upper
/v1/auditledger be retired or reconciled with the shared admin audit-event store? - What route verifies a share token, re-checks current scope, and renders a read-only investigation without leaking the token?
- Which canonical serialization, digest/signature, and verification utility make exported evidence tamper-evident?
- How should the UI expose descriptions, bookmarks, missing events, redaction, retention boundaries, and optimistic concurrency?