Hive Hive
Sign in

feat(audit): admin-only audit trail with agent actor support

GitHub issue · Closed

Metadata
Source
tuist/hive #34
Updated
Jun 24, 2026
Domains
Hive
Details

Summary

Ports the auditing system from ../atlas and lands an admin-only /audit page in Hive, with a Hive-flavored extension for agent actors built around our Condukt workflows.

Why this shape: I wanted parity with Atlas’s data model and UX so we can reason about events the same way across both apps, but Hive’s call sites are different — single-tenant deployment, no executive/employee distinction, MCP- and webhook-heavy, with Condukt-driven agents that act on their own. So the port keeps Atlas’s Hive.Audit context + Activity schema + Noora table almost verbatim and adds a polymorphic actor_kind column on top so an agent run is a first-class kind of actor rather than buried in metadata.

Approaches considered and not taken:

  • A separate agents table for stronger identity per agent. Rejected for now: keying off actor_name is enough until we have many named agents and want “list all activity by IssueTriageAgent” as a real screen.
  • Reusing the email-domain-derived org role (Hive.Auth.role/1) instead of introducing a stored :role. Rejected: org membership and “can see the audit trail” are different concerns; mixing them would force every org member into admin.
  • Setting Audit.put_context/1 in RequireAuthenticated. Rejected: that plug only runs for the / PageController, and adding context there caused a regression in existing tests since the plug must also work for un-fetched-session conns. Context lives in DashboardLive.Hooks (LiveViews) and MCPAuthentication (MCP) instead, both of which already touch the user.

Key pieces:

  • Hive.Audit + Hive.Audit.Activity — process-dictionary actor/interface context, log/2 / record/3 / list_activities/1 / serialize/1. Interfaces are dashboard | mcp | webhook | worker | system. resource_path/3 only emits a path when one resolves (/specs/<number> and /meadows/<id> today), so we don’t show dead links.
  • Agent actor modelactor_kind column (user | agent | system), Hive.Audit.agent_actor(name, model:), and Hive.Agents.Sessions.run/3 wraps every Condukt call in Audit.with_context so events recorded inside an agent’s tools attribute to the agent. The agent’s model lands in metadata["agent_model"].
  • Hive.Audit.Policy (LetMe) + a new stored :role (:admin / :member) on Hive.Accounts.User. AGENTS.md now documents LetMe as the project’s authorization framework and the role distinction.
  • HiveWeb.AuditLive at /audit with a Noora table, filter dropdown (interface, kind, action, target), search, and pagination. Sidebar link is gated on admin?. Actor and Kind live in separate columns so the badge renders inside its own cell padding.
  • MCP tools list_audit_activities and get_audit_activity mirror the LiveView’s filters and gate on the same policy.
  • WiringAudit.record/3 calls at sign-in/out and at spec create/update for an immediately visible trail.

Verification:

  • Promoted test@hive.dev to admin in seeds and ran the seeds to produce 14 demo audit rows spanning every interface, two of which are agent-acted. Loaded /audit in the browser via /dev/login; the table renders, filters work, links resolve.

Testing

  • mix precommit (compile –warnings-as-errors, deps.unlock –unused, format, credo, test) — clean, 333 tests, 0 failures
  • mix run priv/repo/seeds.exs — idempotent re-run produces the demo audit dataset (entries keyed by seed_key in metadata so reruns replace rather than duplicate)
  • Smoked /audit in dev as test@hive.dev (admin) to verify the layout, the kind badge column, the spec/meadow target links, and the filter toolbar
Comments
GA
github-actions[bot] Jun 12, 2026

Blick review didn’t run

The blick review step failed before producing a manifest, so there’s no review to post on this PR. This usually means the agent (opencode) couldn’t start — common causes are an expired or suspended model API key, a missing secret, or the workflow timing out.

See the workflow run for details: https://github.com/tuist/hive/actions/runs/27442866167

Commit: d2a21dd9542216361db0b78a27fc05fbd81ad84b

GA
github-actions[bot] Jun 15, 2026

Blick review didn’t run

The blick review step failed before producing a manifest, so there’s no review to post on this PR. This usually means the agent (opencode) couldn’t start — common causes are an expired or suspended model API key, a missing secret, or the workflow timing out.

See the workflow run for details: https://github.com/tuist/hive/actions/runs/27544041675

Commit: 4e76613990e4744080ab6554c85e19c0fdc4c2c8

GA
github-actions[bot] Jun 15, 2026

Blick review didn’t run

The blick review step failed before producing a manifest, so there’s no review to post on this PR. This usually means the agent (opencode) couldn’t start — common causes are an expired or suspended model API key, a missing secret, or the workflow timing out.

See the workflow run for details: https://github.com/tuist/hive/actions/runs/27546911323

Commit: ab70c1d5975542aa38d46a3892a59e5ac8891c79

GA
github-actions[bot] Jun 15, 2026

Blick review didn’t run

The blick review step failed before producing a manifest, so there’s no review to post on this PR. This usually means the agent (opencode) couldn’t start — common causes are an expired or suspended model API key, a missing secret, or the workflow timing out.

See the workflow run for details: https://github.com/tuist/hive/actions/runs/27550115226

Commit: 62d355b5c1dd476ab22e9a56bd8d409255c2de15

GA
github-actions[bot] Jun 16, 2026

Blick review didn’t run

The blick review step failed before producing a manifest, so there’s no review to post on this PR. This usually means the agent (opencode) couldn’t start — common causes are an expired or suspended model API key, a missing secret, or the workflow timing out.

See the workflow run for details: https://github.com/tuist/hive/actions/runs/27608030894

Commit: a7ced0be0a0c6d1055f5b5ed2c1b8ee05c28b533