Skip to content

feat(audit-log): add System tab for instance-level auth events#31

Merged
tianzhou merged 2 commits into
mainfrom
feat/audit-log-system-tab
Jun 26, 2026
Merged

feat(audit-log): add System tab for instance-level auth events#31
tianzhou merged 2 commits into
mainfrom
feat/audit-log-system-tab

Conversation

@tianzhou

Copy link
Copy Markdown
Contributor

What

Implements two tabs on /audit-log (task #6):

  • Connection — the existing per-connection SQL execution / data export table, unchanged, gated on admin for the selected connection.
  • System — instance-level events not tied to a connection (auth.login / auth.logout), showing actor, status, provider, and source IP. Gated on instance owner.

Why owner-gated (not connection-admin)

Auth events are instance-wide. Gating the System tab on per-connection admin would let an admin of a single connection see everyone's login/IP activity. Instance owner (existing concept: isOwner / session isOwner) is the correct boundary — confirmed with @tianzhou.

Changes

  • proto: GetSystemAuditLogEntries RPC; add provider / ip to AuditLogEntry.
  • server/lib/audit.ts: listSystemAuditEvents(limit) — events with no connection, newest-first, bounded by limit (mirrors listAuditEvents).
  • server/services/query-service.ts: owner-gated getSystemAuditLogEntries handler; extracted a shared toAuditLogEntry mapping (also adds provider/ip to the existing handler).
  • ui: AuditLog.tsx tab switcher; System tab + query only enabled for owners.
  • tests: system-vs-connection separation, system response limit.
  • docs: document the two tabs in docs/features/audit-log.mdx.

Verification

  • pnpm test tests/audit.test.ts — 12 passed
  • pnpm build — passed (pre-existing Vite/Rolldown warnings only)
  • npx eslint on changed frontend files — clean

src/gen is gitignored; CI regenerates from proto/query.proto.

🤖 Generated with Claude Code

The /audit-log page now has two tabs:
- Connection: the existing per-connection SQL execution / data export
  view (unchanged), gated on `admin` for the selected connection.
- System: instance-level events not tied to a connection (auth.login /
  auth.logout) with provider + source IP, gated on instance `owner`.

Owner gating (rather than connection-admin) keeps system-wide login/IP
activity from leaking to an admin of a single connection.

- proto: add GetSystemAuditLogEntries RPC; add provider/ip to AuditLogEntry
- audit: add listSystemAuditEvents() (events with no connection)
- query-service: owner-gated handler; extract shared toAuditLogEntry mapping
- ui: tab switcher; System tab shown only to owners
- tests: system vs connection separation + system limit
- docs: document the two tabs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 26, 2026 05:54
@mintlify

mintlify Bot commented Jun 26, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
pgconsole 🟢 Ready View Preview Jun 26, 2026, 5:54 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pgconsole Ready Ready Preview, Comment Jun 26, 2026 6:07am

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a second “System” tab to the /audit-log page to surface instance-wide authentication events (login/logout) separately from the existing per-connection audit log, with server-side owner gating to avoid exposing login/IP activity to connection-level admins.

Changes:

  • Introduces a new GetSystemAuditLogEntries RPC and extends AuditLogEntry with provider and ip.
  • Adds server-side support for listing non-connection-scoped audit events and enforces instance-owner access for system audit log retrieval.
  • Updates the UI to use a tabbed audit log view (Connection vs System) and adds test coverage + docs for the split.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/audit.test.ts Adds tests for system-vs-connection event separation and system log limiting.
src/pages/AuditLog.tsx Implements tabbed audit log UI and renders system audit table (owner-only).
src/hooks/useQuery.ts Adds a React Query hook + cache key for fetching system audit log entries.
server/services/query-service.ts Adds owner-gated getSystemAuditLogEntries handler and shared event→wire mapping.
server/lib/audit.ts Adds auditLogin/auditLogout events and listSystemAuditEvents(limit) retrieval.
proto/query.proto Adds system audit RPC + request/response messages; adds provider/ip to AuditLogEntry.
docs/features/audit-log.mdx Documents the two audit log tabs and their permission boundaries.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@greptile-apps

greptile-apps Bot commented Jun 26, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a System tab to /audit-log that surfaces instance-level auth events (auth.login / auth.logout) alongside the existing per-connection Connection tab, gating the new tab on the instance owner role rather than per-connection admin to prevent cross-user IP/provider leakage.

  • Proto + server: GetSystemAuditLogEntries RPC with an owner check; provider/ip fields added to AuditLogEntry; shared toAuditLogEntry mapper extracted to remove duplication from getAuditLogEntries.
  • Lib: listSystemAuditEvents filters the in-memory store for events without a connection field, newest-first, bounded by limit — consistent pattern with the existing listAuditEvents.
  • UI: AuditLog.tsx split into a tab layout; useSystemAuditLogEntries hook added with enabled = isOwner so no request is made for non-owners; EntriesPanel component introduced to share loading/error/empty rendering across both tabs.

Confidence Score: 4/5

The owner gate is correctly enforced on both the server and client; no regressions to the existing connection audit log path.

The two minor notes — the implicit connection-field filter and the disabled-query loading state — are speculative future risks rather than current defects, so they do not affect merge safety.

server/lib/audit.ts — the !('connection' in event) predicate in listSystemAuditEvents is the only place worth a second look if new event types are added.

Important Files Changed

Filename Overview
server/lib/audit.ts Adds listSystemAuditEvents — correctly iterates newest-first and filters by absence of a connection field; uses the same prune-then-iterate pattern as the existing listAuditEvents
server/services/query-service.ts Extracts toAuditLogEntry to remove duplication, adds getSystemAuditLogEntries with explicit owner check; security gate is correct and consistent with the existing getAuditLogEntries pattern
src/pages/AuditLog.tsx Introduces tab layout with EntriesPanel abstraction; System tab correctly gated on isOwner on both trigger and content; query disabled for non-owners
src/hooks/useQuery.ts Adds useSystemAuditLogEntries with enabled guard and 5s refetch interval, consistent with the existing useAuditLogEntries pattern
proto/query.proto Adds GetSystemAuditLogEntries RPC and provider/ip fields to AuditLogEntry; field numbering is sequential and non-conflicting
tests/audit.test.ts Adds two targeted tests: system/connection separation and limit enforcement on system events; covers the new listSystemAuditEvents code path well
docs/features/audit-log.mdx Docs updated to describe both tabs and their access requirements; accurate and concise

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Browser
    participant AuditLog.tsx
    participant useSystemAuditLogEntries
    participant getSystemAuditLogEntries
    participant listSystemAuditEvents

    Browser->>AuditLog.tsx: render /audit-log
    AuditLog.tsx->>AuditLog.tsx: "useOwner() -> isOwner"
    alt "isOwner = true"
        AuditLog.tsx->>useSystemAuditLogEntries: "enabled=true"
        useSystemAuditLogEntries->>getSystemAuditLogEntries: limit 100
        getSystemAuditLogEntries->>getSystemAuditLogEntries: isOwner(email) check
        getSystemAuditLogEntries->>listSystemAuditEvents: "limit=100"
        listSystemAuditEvents-->>getSystemAuditLogEntries: auth events newest-first
        getSystemAuditLogEntries-->>useSystemAuditLogEntries: AuditLogEntry[]
        useSystemAuditLogEntries-->>AuditLog.tsx: data
        AuditLog.tsx->>Browser: System tab with entries
    else "isOwner = false"
        AuditLog.tsx->>useSystemAuditLogEntries: "enabled=false"
        AuditLog.tsx->>Browser: Connection tab only
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Browser
    participant AuditLog.tsx
    participant useSystemAuditLogEntries
    participant getSystemAuditLogEntries
    participant listSystemAuditEvents

    Browser->>AuditLog.tsx: render /audit-log
    AuditLog.tsx->>AuditLog.tsx: "useOwner() -> isOwner"
    alt "isOwner = true"
        AuditLog.tsx->>useSystemAuditLogEntries: "enabled=true"
        useSystemAuditLogEntries->>getSystemAuditLogEntries: limit 100
        getSystemAuditLogEntries->>getSystemAuditLogEntries: isOwner(email) check
        getSystemAuditLogEntries->>listSystemAuditEvents: "limit=100"
        listSystemAuditEvents-->>getSystemAuditLogEntries: auth events newest-first
        getSystemAuditLogEntries-->>useSystemAuditLogEntries: AuditLogEntry[]
        useSystemAuditLogEntries-->>AuditLog.tsx: data
        AuditLog.tsx->>Browser: System tab with entries
    else "isOwner = false"
        AuditLog.tsx->>useSystemAuditLogEntries: "enabled=false"
        AuditLog.tsx->>Browser: Connection tab only
    end
Loading

Reviews (1): Last reviewed commit: "feat(audit-log): add System tab for inst..." | Re-trigger Greptile

Comment thread server/lib/audit.ts Outdated
Comment thread src/pages/AuditLog.tsx Outdated
- listSystemAuditEvents: match system events by explicit action
  (auth.login/auth.logout) instead of the absence of a `connection`
  field, so a future connection-less event can't leak into the System tab.
- EntriesPanel: only show the loading state while actually fetching;
  a disabled query (non-owner) now falls through to the empty state
  instead of spinning forever.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tianzhou tianzhou merged commit 7afafbf into main Jun 26, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants