Repository navigation
Description
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_activitycascade (retain rows withset nullagainst a nullableworkspaceIdtombstone 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
:deletepermissions" recipe.workspace_roles/workspace_role_permissions+assertPermissionalready support this — it works today in code; it's just undiscoverable. feat: Archive/Trash for cards #420's separatecard:archivepermission (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