8000 Audit-trail integrity: workspace hard-delete cascades destroy card_activity (incl. with #420); activity immutability & scoped agent keys · Issue #611 · kanbn/kan · GitHub
[go: up one dir, main page]

Skip to content

Audit-trail integrity: workspace hard-delete cascades destroy card_activity (incl. with #420); activity immutability & scoped agent keys #611

Open

Description

@aficiomaquinas

Context

I'm evaluating kan.bn as the system of record for human + AI-agent collaboration, where the activity log is the compliance artifact (who changed what, when — never silently destroyed). Verified against main @ 3039670 and reviewed the #420 patch: kan.bn is closer to this than most tools — card_activity records field-level from/to transitions with createdBy, survives soft deletes, and #420 adds the full archive → trash → restore lifecycle (card.deleted, card.restored, card.unarchived). Thank you — genuinely well done.

Three gaps remain for audit-first deployments:

1. workspace:delete hard-deletes the entire audit trail

workspaceRepo.hardDelete (packages/db/src/repository/workspace.repo.ts, hardDelete) issues a real SQL DELETE, and card_activity.cardId (plus the from/to list/label FKs) is onDelete: "cascade" (packages/db/src/schema/cards.ts). Net effect: deleting a workspace wipes every activity record for every card it ever contained — one API call, also exposed via the official MCP as delete_workspace. #420 does not change this (it doesn't touch the workspace path).

Suggestions (any one would fix it):

  • Soft-delete workspaces like boards/lists/cards (consistent with the rest of the codebase), or
  • Keep hard delete but break the card_activity cascade (retain rows with set null against a nullable workspaceId tombstone column), or
  • Require a typed confirmation / export-first step for workspace delete.

Note: once #420 56EF merges, card/list/board deletion becomes recoverable and the MCP's destructive surface shrinks to effectively delete_workspace + trash purge — which narrows the audit attack surface to a single call. Fixing this closes it.

2. Activity rows are mutable with no guard

card_activity has no immutability story: any code path (or future plugin/contributor) can UPDATE/DELETE rows and nothing flags it. Minimal ask: document an invariant ("activity rows are append-only; PRs must not mutate them") plus a repo-level guard (test or lint rule). Longer term: a DB trigger or RLS policy preventing UPDATE/DELETE on card_activity for non-service roles would make the guarantee structural.

3. Agent credentials are all-or-nothing

The official MCP authenticates with a user's KAN_API_TOKEN (Better Auth) — great for attribution (createdBy distinguishes the agent in the trail), but the token carries the user's full power: destructive tools (delete_workspace, delete_board, remove_member, ...) are available to any agent key.

Two complementary directions:

  • Docs pattern: document the "agent user + custom workspace role without :delete permissions" recipe. workspace_roles/workspace_role_permissions + assertPermission already support this — it works today in code; it's just undiscoverable. feat: Archive/Trash for cards #420's separate card:archive permission (and its grant-backfill migration) is exactly the right direction — the same treatment for trash-purge would complete the story.
  • Scoped API keys: optional per-key scopes (read-only, tool allow-list), similar to GitHub fine-grained tokens. If Better Auth's API-key plugin grows key-level permissions, wiring that into the MCP would be a real differentiator for the AI-agent niche the MCP already targets.

Related: agents can currently read the full activity trail via get_card_activities; a scope to restrict activity reads would let operators treat the trail as tamper-evident to the agents themselves.

Use case

Self-hosted, single operator + several AI agents under distinct credentials, audit-first: certainty and no surprises about who changed what, with the requirement that no agent (even inadvertently) can erase history. Happy to test PRs against that setup — it's a good stress test for the RBAC + MCP work already shipping here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0