[{"data":1,"prerenderedAt":723},["ShallowReactive",2],{"navigation":3,"\u002Freference\u002Factivity-log":189,"\u002Freference\u002Factivity-log-surround":718},[4,23,40,51,74,116,157,177],{"title":5,"path":6,"stem":7,"children":8,"icon":22},"Get Started","\u002Fget-started","1.get-started\u002F1.index",[9,12,17],{"title":10,"path":6,"stem":7,"icon":11},"Introduction","i-lucide-house",{"title":13,"path":14,"stem":15,"icon":16},"Prerequisites","\u002Fget-started\u002Fprerequisites","1.get-started\u002F2.prerequisites","i-lucide-list-checks",{"title":18,"path":19,"stem":20,"icon":21},"Installation","\u002Fget-started\u002Finstallation","1.get-started\u002F3.installation","i-lucide-settings","i-lucide-rocket",{"title":24,"icon":25,"path":26,"stem":27,"children":28,"page":39},"Develop","i-lucide-code","\u002Fdevelop","2.develop",[29,34],{"title":30,"path":31,"stem":32,"icon":33},"Version Control","\u002Fdevelop\u002Fversion-control","2.develop\u002F1.version-control","i-lucide-git-branch",{"title":35,"path":36,"stem":37,"icon":38},"Claude Code","\u002Fdevelop\u002Fclaude-code","2.develop\u002F2.claude-code","i-lucide-sparkles",false,{"title":41,"icon":42,"path":43,"stem":44,"children":45,"page":39},"Launch","i-lucide-globe","\u002Flaunch","3.launch",[46],{"title":47,"path":48,"stem":49,"icon":50},"Cloudflare","\u002Flaunch\u002Fcloudflare","3.launch\u002F1.cloudflare","i-lucide-cloud-upload",{"title":52,"path":53,"stem":54,"children":55,"icon":73},"Plugins","\u002Fplugins","4.plugins\u002F1.index",[56,58,63,68],{"title":52,"path":53,"stem":54,"icon":57},"i-lucide-list",{"title":59,"path":60,"stem":61,"icon":62},"Public API","\u002Fplugins\u002Fapi-keys","4.plugins\u002F2.api-keys","i-lucide-key",{"title":64,"path":65,"stem":66,"icon":67},"Cron Jobs","\u002Fplugins\u002Fcron-jobs","4.plugins\u002F4.cron-jobs","i-lucide-clock",{"title":69,"path":70,"stem":71,"icon":72},"Rate Limiting","\u002Fplugins\u002Frate-limiting","4.plugins\u002F5.rate-limiting","i-lucide-gauge","i-lucide-puzzle",{"title":75,"path":76,"stem":77,"children":78,"icon":115},"Examples","\u002Fexamples","5.examples\u002F1.index",[79,80,85,90,95,100,105,110],{"title":75,"path":76,"stem":77,"icon":57},{"title":81,"path":82,"stem":83,"icon":84},"Job Management","\u002Fexamples\u002Fjob-management","5.examples\u002F2.job-management","i-lucide-briefcase",{"title":86,"path":87,"stem":88,"icon":89},"Kanban \u002F To-Do List","\u002Fexamples\u002Fkanban-todo","5.examples\u002F3.kanban-todo","i-lucide-kanban",{"title":91,"path":92,"stem":93,"icon":94},"Inventory Management","\u002Fexamples\u002Finventory-management","5.examples\u002F4.inventory-management","i-lucide-package",{"title":96,"path":97,"stem":98,"icon":99},"Mini CRM","\u002Fexamples\u002Fmini-crm","5.examples\u002F5.mini-crm","i-lucide-users",{"title":101,"path":102,"stem":103,"icon":104},"Sales Orders & Invoices","\u002Fexamples\u002Fsales-invoices","5.examples\u002F6.sales-invoices","i-lucide-receipt",{"title":106,"path":107,"stem":108,"icon":109},"Calendar & Booking","\u002Fexamples\u002Fcalendar-booking","5.examples\u002F7.calendar-booking","i-lucide-calendar",{"title":111,"path":112,"stem":113,"icon":114},"Support Tickets","\u002Fexamples\u002Fsupport-tickets","5.examples\u002F8.support-tickets","i-lucide-life-buoy","i-lucide-book-open",{"title":117,"icon":118,"path":119,"stem":120,"children":121,"page":39},"Reference","i-lucide-file-text","\u002Freference","6.reference",[122,127,132,137,142,147,152],{"title":123,"path":124,"stem":125,"icon":126},"Architecture","\u002Freference\u002Farchitecture","6.reference\u002F1.architecture","i-lucide-layers",{"title":128,"path":129,"stem":130,"icon":131},"Permissions","\u002Freference\u002Fpermissions","6.reference\u002F2.permissions","i-lucide-shield",{"title":133,"path":134,"stem":135,"icon":136},"Invitations","\u002Freference\u002Finvitations","6.reference\u002F3.invitations","i-lucide-mail",{"title":138,"path":139,"stem":140,"icon":141},"Webhooks","\u002Freference\u002Fwebhooks","6.reference\u002F4.webhooks","i-lucide-webhook",{"title":143,"path":144,"stem":145,"icon":146},"AI Chat","\u002Freference\u002Fai-chat","6.reference\u002F5.ai-chat","i-lucide-message-square",{"title":148,"path":149,"stem":150,"icon":151},"Activity Log","\u002Freference\u002Factivity-log","6.reference\u002F6.activity-log","i-lucide-scroll",{"title":153,"path":154,"stem":155,"icon":156},"Manual Setup","\u002Freference\u002Fmanual-setup","6.reference\u002F7.manual-setup","i-lucide-wrench",{"title":158,"icon":159,"path":160,"stem":161,"children":162,"page":39},"Legal","i-lucide-scale","\u002Flegal","7.legal",[163,168,172],{"title":164,"path":165,"stem":166,"icon":167},"License","\u002Flegal\u002Flicense","7.legal\u002F1.license","i-lucide-file-check",{"title":169,"path":170,"stem":171,"icon":118},"Terms and Conditions","\u002Flegal\u002Fterms","7.legal\u002F2.terms",{"title":173,"path":174,"stem":175,"icon":176},"Privacy Policy","\u002Flegal\u002Fprivacy","7.legal\u002F3.privacy","i-lucide-lock",{"title":178,"path":179,"stem":180,"children":181,"icon":183},"Upgrades","\u002Fupgrades","8.upgrades\u002F1.index",[182,184],{"title":178,"path":179,"stem":180,"icon":183},"i-lucide-arrow-up-circle",{"title":185,"path":186,"stem":187,"icon":188},"\u002Fapp\u002F* gated subtree routing","\u002Fupgrades\u002Fapp-subtree-routing","8.upgrades\u002F2.app-subtree-routing","i-lucide-route",{"id":190,"title":148,"body":191,"description":711,"extension":712,"links":713,"meta":714,"navigation":715,"path":149,"seo":716,"stem":150,"__hash__":717},"docs\u002F6.reference\u002F6.activity-log.md",{"type":192,"value":193,"toc":702},"minimark",[194,211,216,223,299,303,317,392,425,436,440,443,459,468,477,483,493,498,512,606,609,613,644,648,698],[195,196,197,198,202,203,206,207,210],"p",{},"Every write to an opted-in table is captured by the ",[199,200,201],"code",{},"log_activity()","\nPostgres AFTER trigger — actor, source, before\u002Fafter snapshot, and a\nper-column diff for updates. The log is visible at ",[199,204,205],{},"\u002Fapp\u002Factivity"," to team\nowners only (",[199,208,209],{},"activity.view"," permission).",[212,213,215],"h2",{"id":214},"what-gets-captured","What gets captured",[195,217,218,219,222],{},"Each row in ",[199,220,221],{},"activity_log",":",[224,225,226,233,243,265,274,287],"ul",{},[227,228,229,232],"li",{},[199,230,231],{},"team_id"," — team the change belongs to (never null; rows that can't be\nattributed are dropped by the trigger)",[227,234,235,238,239,242],{},[199,236,237],{},"actor_id"," — user who initiated the change. Null when the write came\nfrom a service-role path that bypassed ",[199,240,241],{},"authUser"," (treat as \"system\nwrote this — investigate\")",[227,244,245,248,249,252,253,256,257,260,261,264],{},[199,246,247],{},"actor_source"," — ",[199,250,251],{},"'api'"," (UI or external), ",[199,254,255],{},"'chat'"," (AI assistant\ntool call), or ",[199,258,259],{},"'system'"," (trigger saw no ",[199,262,263],{},"x-actor-source"," header)",[227,266,267,270,271],{},[199,268,269],{},"source_ref"," — context-specific pointer, e.g. the chat ID when\n",[199,272,273],{},"actor_source = 'chat'",[227,275,276,279,280,279,283,286],{},[199,277,278],{},"table_name",", ",[199,281,282],{},"row_id",[199,284,285],{},"action"," — what changed",[227,288,289,279,292,279,295,298],{},[199,290,291],{},"before",[199,293,294],{},"after",[199,296,297],{},"changed"," — full row snapshots and per-column diff",[212,300,302],{"id":301},"how-it-knows-who-did-what","How it knows who did what",[195,304,305,308,309,312,313,316],{},[199,306,307],{},"authUser()"," \u002F ",[199,310,311],{},"authUserOnly()"," return a Supabase client built by\n",[199,314,315],{},"createAuditedClient({ actorId, teamId, source: 'api' })",". That client\nattaches three headers to every PostgREST request:",[318,319,320,333],"table",{},[321,322,323],"thead",{},[324,325,326,330],"tr",{},[327,328,329],"th",{},"Header",[327,331,332],{},"Value",[334,335,336,347,360,378],"tbody",{},[324,337,338,344],{},[339,340,341],"td",{},[199,342,343],{},"x-actor-id",[339,345,346],{},"The authenticated user's UUID",[324,348,349,354],{},[339,350,351],{},[199,352,353],{},"x-team-id",[339,355,356,357,359],{},"Active team (fallback when row has no ",[199,358,231],{}," column)",[324,361,362,366],{},[339,363,364],{},[199,365,263],{},[339,367,368,279,371,374,375],{},[199,369,370],{},"api",[199,372,373],{},"chat",", or ",[199,376,377],{},"system",[324,379,380,385],{},[339,381,382],{},[199,383,384],{},"x-actor-ref",[339,386,387,388,391],{},"Optional reference, e.g. ",[199,389,390],{},"\u003CchatId>"," for chat",[195,393,394,395,398,399,279,401,403,404,406,407,410,411,414,415,308,418,421,422,424],{},"The trigger reads ",[199,396,397],{},"current_setting('request.headers')"," to populate\n",[199,400,237],{},[199,402,247],{},", and ",[199,405,269],{},". Any write not routed\nthrough ",[199,408,409],{},"createAuditedClient"," (e.g. a direct ",[199,412,413],{},"serverSupabaseServiceRole","\ncall) produces a row with ",[199,416,417],{},"actor_id = null",[199,419,420],{},"actor_source = 'system'","\n— that's your signal that someone bypassed ",[199,423,241],{},".",[195,426,427,428,431,432,435],{},"Chat endpoints override the default: ",[199,429,430],{},"server\u002Fapi\u002Fchats\u002F[id].post.ts","\nbuilds its own client with ",[199,433,434],{},"createAuditedClient({ source: 'chat', sourceRef: chatId })",", so every tool-driven write is tagged with the\nchat that caused it.",[212,437,439],{"id":438},"opting-a-new-table-in","Opting a new table in",[195,441,442],{},"One line in a migration:",[444,445,450],"pre",{"className":446,"code":447,"language":448,"meta":449,"style":449},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","select enable_activity_log('tasks');\n","sql","",[199,451,452],{"__ignoreMap":449},[453,454,457],"span",{"class":455,"line":456},"line",1,[453,458,447],{},[195,460,461,462,464,465,467],{},"To exclude sensitive columns from the ",[199,463,291],{},"\u002F",[199,466,294],{}," snapshots:",[444,469,471],{"className":446,"code":470,"language":448,"meta":449,"style":449},"select enable_activity_log('api_keys', exclude_cols => array['key_hash']);\n",[199,472,473],{"__ignoreMap":449},[453,474,475],{"class":455,"line":456},[453,476,470],{},[195,478,479,482],{},[199,480,481],{},"enable_activity_log"," is idempotent — it drops any existing trigger on\nthe target table first, so you can re-run it safely.",[195,484,485,486,308,489,308,491,424],{},"No-op updates (same row upserted with identical values) are detected by\nthe trigger and suppressed. Excluded columns never reach\n",[199,487,488],{},"activity_log.before",[199,490,294],{},[199,492,297],{},[494,495,497],"h3",{"id":496},"add-a-ui-filter-entry","Add a UI filter entry",[195,499,500,501,503,504,507,508,511],{},"The ",[199,502,205],{}," page exposes a table filter dropdown populated from the\n",[199,505,506],{},"tableItems"," array in ",[199,509,510],{},"app\u002Fcomponents\u002Factivity\u002FList.vue",". Whenever you\nopt a new table in, add an entry there:",[444,513,517],{"className":514,"code":515,"language":516,"meta":449,"style":449},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const tableItems = [\n  \u002F\u002F ...\n  { label: \"Tasks\", value: \"tasks\", icon: \"i-lucide-check-square\" },\n];\n","ts",[199,518,519,536,543,597],{"__ignoreMap":449},[453,520,521,525,529,533],{"class":455,"line":456},[453,522,524],{"class":523},"spNyl","const",[453,526,528],{"class":527},"sTEyZ"," tableItems ",[453,530,532],{"class":531},"sMK4o","=",[453,534,535],{"class":527}," [\n",[453,537,539],{"class":455,"line":538},2,[453,540,542],{"class":541},"sHwdD","  \u002F\u002F ...\n",[453,544,546,549,553,555,558,562,565,568,571,573,575,578,580,582,585,587,589,592,594],{"class":455,"line":545},3,[453,547,548],{"class":531},"  {",[453,550,552],{"class":551},"swJcz"," label",[453,554,222],{"class":531},[453,556,557],{"class":531}," \"",[453,559,561],{"class":560},"sfazB","Tasks",[453,563,564],{"class":531},"\"",[453,566,567],{"class":531},",",[453,569,570],{"class":551}," value",[453,572,222],{"class":531},[453,574,557],{"class":531},[453,576,577],{"class":560},"tasks",[453,579,564],{"class":531},[453,581,567],{"class":531},[453,583,584],{"class":551}," icon",[453,586,222],{"class":531},[453,588,557],{"class":531},[453,590,591],{"class":560},"i-lucide-check-square",[453,593,564],{"class":531},[453,595,596],{"class":531}," },\n",[453,598,600,603],{"class":455,"line":599},4,[453,601,602],{"class":527},"]",[453,604,605],{"class":531},";\n",[195,607,608],{},"Without this entry, the log still captures writes to the table, but the\nfilter dropdown won't let owners narrow down to it.",[494,610,612],{"id":611},"global-tables","Global tables",[195,614,615,616,618,619,622,623,627,628,631,632,635,636,640,641,424],{},"Tables without a ",[199,617,231],{}," column (e.g. ",[199,620,621],{},"profiles",") are intentionally\n",[624,625,626],"strong",{},"not"," opted in by default — attributing a profile name change to one\nof N teams would be misleading. The heavy, noisy ",[199,629,630],{},"chats"," and\n",[199,633,634],{},"chat_messages"," tables are also excluded: the useful audit trail is\ncaptured on the ",[637,638,639],"em",{},"target"," table of each tool call, tagged with\n",[199,642,643],{},"actor_source='chat'",[212,645,647],{"id":646},"using-the-log","Using the log",[224,649,650,665,677],{},[227,651,652,248,655,657,658,660,661,664],{},[624,653,654],{},"UI",[199,656,205],{}," shows every row for the current team. Owners only\n(enforced by RLS + the ",[199,659,209],{}," permission + the\n",[199,662,663],{},"requirePermission"," middleware on the page).",[227,666,667,248,670,672,673,676],{},[624,668,669],{},"AI chat",[199,671,221],{}," is registered in ",[199,674,675],{},"tablePermissions"," as\nread-only for owners. The LLM can answer \"what changed in the last\nhour?\" style questions for owners and is blocked for admins\u002Fmembers.",[227,678,679,682,683,464,685,464,687,689,690,693,694,697],{},[624,680,681],{},"SQL"," — the ",[199,684,291],{},[199,686,294],{},[199,688,297],{}," columns are heavy JSON. Don't\n",[199,691,692],{},"select *"," — project specific keys (",[199,695,696],{},"before->>'title'",") or aggregate.",[699,700,701],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}",{"title":449,"searchDepth":456,"depth":538,"links":703},[704,705,706,710],{"id":214,"depth":538,"text":215},{"id":301,"depth":538,"text":302},{"id":438,"depth":538,"text":439,"children":707},[708,709],{"id":496,"depth":545,"text":497},{"id":611,"depth":545,"text":612},{"id":646,"depth":538,"text":647},"Trigger-driven audit of every mutation on opted-in tables.","md",null,{},{"icon":151},{"title":148,"description":711},"R26t-DUE7904ZDTskx6XXdnqcUXCypKoN1kXInRBt10",[719,721],{"title":143,"path":144,"stem":145,"description":720,"icon":146,"children":-1},"How the baked-in AI assistant works, and how to make new tables available to it.",{"title":153,"path":154,"stem":155,"description":722,"icon":156,"children":-1},"The fallback setup flow — clone the template and wire up Supabase by hand, without the CLI.",1777092171564]