Cloudsteading
FeedLog public feedback board with categories, votes and request statuses
FeedLog public feedback board with categories, votes and request statuses

FeedLog

Own your feedback board, votes and public roadmap on Cloudflare Workers

FeedLog is a self-hosted Canny/Featurebase alternative built on Cloudflare (Hyperdrive, Queues, R2, Workers). Paid services required. Inspect the source and license in the linked repository.

Source & license

Upstream license: MIT

License TL;DR

You can use it, change it, self-host it and sell it. Keep the original copyright and license notice with copies of the code. You don’t have to publish your changes. The authors don’t promise it will work.

Explain MIT in plain English →

Summary of the main license. Separate packages and assets can have different terms.

Inspect repository ↗Read this project’s actual license ↗

Repository owner

@linkcraftstudio

See the upstream repository for the original creator and contributors.

Maintain this project? Maintainer verification →

Cloudflare hosting

Paid services required

Plan for Workers Paid at US$5 per account/month plus usage, and external PostgreSQL 17+ with vector through Hyperdrive. Neon Free is a limited database option (100 CU-hours/month, 0.5 GB storage, 5 GB transfer per project). R2 uploads, optional AI/email and maintenance need separate budgets; no zero-cost or measured-bill claim.

Hosting requirements
  • The Workers Paid minimum is account-wide, not a separate $5 subscription for every app; usage beyond included allowances can add charges.
  • Neon Free provides 100 CU-hours monthly, 0.5 GB storage and 5 GB network transfer per project; select PostgreSQL 17+ and enable vector before the first migration.
  • A continuously busy database or retained feedback, attachments and embeddings can exhaust database allowances; check actual provider usage.
  • Hyperdrive connection pooling and caching are included in Workers Paid; the external database is a separate service.
  • Configure the feedlog-inbox Queue producer and consumer; writes, reads, deletes and retries consume operations.
  • R2 BLOB uploads are optional at the application level. Standard R2 included storage is 10 GB-month per month, with request allowances and metered usage beyond limits.
  • OpenAI-powered features require provider credentials and models. Resend email requires separate provider setup; neither is included in the Workers subscription.
  • Without OAuth, email/password sign-in defaults on. Without Resend, email verification defaults off and password-reset email is unavailable; choose authentication settings deliberately.
  • A real Hyperdrive ID, public auth URL, auth secret and admin-bootstrap emails are required; shipped values are placeholders.
  • This is a source-based planning budget, not a fresh deployment, performance benchmark, security audit or guaranteed bill.
Check current pricing ↗
Review findings & limitations
  • Source and public-demo review only; no fresh installation, migration, authenticated write, AI benchmark or measured bill.
  • Mandatory external PostgreSQL 17+ with vector; database quotas and availability are separate from Cloudflare.
  • Workers Paid is the conservative baseline; the application has not been benchmarked against Workers Free CPU limits.
  • Optional AI and email require configured providers; no provider-free feature parity or competitor savings claimed.
Sources checked 01/10/2026

Repository snapshot: 697d3cc. Hosting eligibility reflects the deployment documentation and listed assumptions.

  • canny ↗

    **Open-source feedback & roadmap tool. Self-host on Cloudflare Workers, Vercel, or Docker.**

  • featurebase ↗

    **Open-source feedback & roadmap tool. Self-host on Cloudflare Workers, Vercel, or Docker.**

  • workers ↗

    name = "feedlog" compatibility_date = "2026-03-01" compatibility_flags = ["nodejs_compat"]

  • hyperdrive ↗

    [[hyperdrive]] binding = "POSTGRES" id = "REPLACE_WITH_YOUR_HYPERDRIVE_ID"

  • r2 ↗

    [[r2_buckets]] binding = "BLOB" bucket_name = "feedlog"

  • queues ↗

    [[queues.producers]] binding = "INBOX_QUEUE" queue = "feedlog-inbox"

  • paid ↗

    The Workers Paid plan includes Workers, Pages Functions, Workers KV, Hyperdrive, and Durable Objects usage for a minimum charge of $5 USD per month for an account.

  • paid ↗

    100 CU-hrs monthly per project

  • paid ↗

    0.5 GB of storage per project

  • paid ↗

    5 GB per project included

  • paid ↗

    | Extension | PG14 | PG15 | PG16 | PG17 | PG18 | Notes |

  • paid ↗

    | [pgvector](https://neon.com/docs/extensions/pgvector) | 0.8.0 | 0.8.0 | 0.8.0 | 0.8.0 | 0.8.6 | Install with `CREATE EXTENSION vector;` |

  • paid ↗

    Hyperdrive is included in both the Free and Paid [Workers plans](https://developers.cloudflare.com/workers/platform/pricing/).

  • paid ↗

    | Standard operations | 10,000 operations/day included | 1,000,000 operations/month included + $0.40/million operations |

  • paid ↗

    | Storage | 10 GB-month / month |

  • paid ↗

    Skipping this disables file uploads but the app still works.

  • MIT ↗

    MIT License Copyright (c) 2026 LinkCraft Studio Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

  • architecture ↗

    "build:cf": "NITRO_PRESET=cloudflare-module NODE_OPTIONS=\"$NODE_OPTIONS --max-old-space-size=8192\" pnpm build",

  • architecture ↗

    "deploy:cf": "wrangler deploy --cwd .output",

  • architecture ↗

    const binding = (globalThis as any).__env__?.POSTGRES || (globalThis as any).POSTGRES

  • architecture ↗

    const { session, orgId } = await requireAuthInOrg(event)

  • architecture ↗

    const created = await createPostRecord({

  • architecture ↗

    export default defineEventHandler

  • architecture ↗

    nitroApp.hooks.hook('cloudflare:queue', async ({ batch, env }) => {

  • architecture ↗

    message.retry({ delaySeconds: 60 })

  • architecture ↗

    meta: { name: 'inbox:schedule', description: 'Recover missing delayed Inbox deliveries' }

  • architecture ↗

    crons = ["0 3 * * *"]

  • architecture ↗

    const requireEmailVerification = parseBool(env.AUTH_EMAIL_VERIFY) ?? hasEmailProvider

  • architecture ↗

    Delivery is best-effort: listeners run outside the response lifecycle, a

  • architecture ↗

    await generatePostEmbedding(row.id, domainEvent.orgId, row.title, row.content, row.contentHash)

Documented public demo screenshot · linkcraftstudio/feedlog repository contributors ↗. Depicts the upstream project. We have not deployed and tested a fresh installation here.

What it can replace

Compare the workflow you need. These mappings describe overlap; full feature parity requires a separate comparison.

Canny logoCanny ↗

Editorial workflow alternative: Feedback board and public roadmap. No complete feature parity, migration compatibility or price saving asserted.

See supporting source ↗
FeedLog public roadmap with planned, in-progress and completed feedback
external SaaS target
varies
external SaaS target
varies

How it works

The shape of FeedLog on Cloudflare, and how it stacks up against the rented tools it replaces.

Architecture

Diagram based on the linked repository documentation. See the sources and hosting assumptions above.

View upstream source ↗
Public interface
Public feedback and authenticated actions6
GET
/api/posts
Read the feedback board
POST
/api/posts
Authenticate, validate and persist a new idea
POST
/api/posts/:postId/vote
Vote on feedback
GET
/api/roadmap
Planned, in-progress and completed posts
GET
/api/changelogs
Read release notes
CRON
0 3 * * *
UTC inbox delivery recovery
↓
App
FeedLog Worker
entry
Nuxt 4 / Nitro cloudflare-module / H3
Serve board, roadmap and changelogAuthenticate and persist feedback/votesConsume delayed inbox Queue messagesRun daily inbox recovery at 03:00 UTC
Supporting services
External PostgreSQL 17+ with vector
backing
Hyperdrive connects to your provider; Neon Free is a limited option
Store users, feedback, votes and roadmap statePersist inbox scheduling stateStore vectors when AI embeddings are configured
Optional AI and email providers
backing
OpenAI models and Resend; separate credentials and costs
AI workflows require configured keys/modelsEmail delivery requires configured providerBest-effort feedback listeners stay off response path
↓

Configuration and workflow sources

Reviewed commit 697d3cc7becc. This configuration evidence comes from reading source files. Execution checks, when available, appear in the project's runtime review.

Deployment configuration · 1 files
wrangler.toml ↗

Cloudflare Workers · compatibility 2026-03-01

feedlog · default

Cron triggers (UTC): 0 3 * * *

  • BLOB → R2
  • POSTGRES → Hyperdrive
  • INBOX_QUEUE → Queues (producer) · queue feedlog-inbox
  • feedlog-inbox → Queues (consumer) · queue feedlog-inbox

Named environments are separate deployments. Bindings are shown only where declared. Configured routes are URL patterns, not verified application endpoints.

Runtime source · handlers, binding usage and workflow steps

Observed TypeScript/JavaScript declarations from Worker entrypoints and resolved relative imports. Calls and workflow steps may run conditionally; their listed order is not a proven end-to-end request flow. Router declarations may be mounted under a prefix or may not be registered. This shows code wiring, not a successful deployment or runtime test. Dynamic wiring, aliases and generated code may not resolve.

modules/blob-s3/module.ts ↗

    Environment references: process.env.NITRO_PRESET

    modules/blob-s3/runtime/server/plugins/blob-s3.ts ↗
    • L12 · isBucketEndpoint calls (conditional paths may differ): hostname.startsWith

    Environment references: process.env.S3_ACCESS_KEY_ID · process.env.S3_SECRET_ACCESS_KEY · process.env.S3_BUCKET · process.env.S3_ENDPOINT · process.env.S3_REGION

    modules/cf-setup/runtime/server/routes/setup.get.ts ↗
    • L222 · safeRedirectTarget calls (conditional paths may differ): getQuery, candidate.startsWith
    modules/cf-setup/runtime/server/middleware/migrate-check.ts ↗
    • L19 · shouldBypass calls (conditional paths may differ): BYPASS_PREFIXES.some, prefix.endsWith, path.startsWith
    modules/cf-setup/runtime/server/utils/connection-string.ts ↗

      Environment references: process.env.DATABASE_URL

      modules/cf-setup/runtime/server/utils/migration-state.ts ↗
      • L32 · readJournalEntries calls (conditional paths may differ): fs.readFileSync, JSON.parse, Array.isArray
      • L42 · probeDatabaseState calls (conditional paths may differ): readJournalEntries, postgres, drizzle, db.transaction, tx.execute, String, client.end
      • L88 · resolveState calls (conditional paths may differ): probeDatabaseState, resolveConnectionString
      modules/widget-preload/module.ts ↗
      • L29 · collectModules calls (conditional paths may differ): seen.has, seen.add, collectModules
      • L37 · collectFiles calls (conditional paths may differ): files.push
      • L47 · collectHints calls (conditional paths may differ): find, Object.keys, ROUTE_MODULE_RE.test, Object.entries, collectModules, collectFiles, I18N_CONFIG_RE.test, shipped.has, shipped.add, hints.push, file.endsWith
      modules/widget-preload/runtime/server/plugins/widget-embed-preload.ts ↗
      • L23 · embedLocale calls (conditional paths may differ): filter, split, path.replace, segments.shift, segments.join, locales.includes
      nuxt.config.ts ↗

        Environment references: process.env.NITRO_PRESET · process.env.VERCEL · process.env.NUXT_PUBLIC_UPLOAD_PREFIX

        server/api/admin/boards/[id].delete.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, getRouterParam, useDB, where, set, db.update, and, eq, returning, db.delete, createError, setResponseStatus
        server/api/admin/boards/reorder.patch.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, readValidatedBody, useDB, Promise.all, body.ids.map, where, set, db.update, and, eq, setResponseStatus
        server/api/admin/boards/[id].patch.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, getRouterParam, readValidatedBody, Object.keys, createError, useDB, returning, where, set, db.update, and, eq
        server/api/admin/boards/index.post.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, readValidatedBody, useDB, where, from, db.select, eq, returning, values, db.insert, setResponseStatus
        server/api/admin/changelogs/[id].delete.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, getRouterParam, useDB, where, db.delete, eq, returning, and, createError, setResponseStatus
        server/api/admin/changelogs/[id].get.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgMember, getRouterParam, useDB, limit, where, from, db.select, and, eq, createError
        server/api/admin/changelogs/ai/generate.post.ts ↗
        • L10 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, readValidatedBody, createError, replace, useDB, where, from, db.select, and, eq, inArray, feedback.push, buildSystemPrompt, buildUserPrompt, client.chat.completions.create, parseAiResponse, parsed.suggested_tags.filter, validTags.has, parsed.title.slice

        Environment references: process.env.OPENAI_API_KEY · process.env.OPENAI_BASE_URL · process.env.OPENAI_TEXT_MODEL

        server/api/admin/changelogs/[id].patch.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, getRouterParam, readValidatedBody, useDB, limit, where, from, db.select, and, eq, createError, returning, set, db.update
        server/api/admin/changelogs/index.get.ts ↗
        • L8 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgMember, getQuery, Math.max, Number, Math.min, useDB, eq, conditions.push, or, and, where, from, db.select, offset, limit, orderBy, leftJoin, desc, rows.map
        server/api/admin/changelogs/[id]/publish.post.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, getRouterParam, useDB, limit, where, from, db.select, and, eq, createError, returning, set, db.update
        server/api/admin/changelogs/index.post.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, readValidatedBody, useDB, generateChangelogSlug, returning, values, db.insert, setResponseStatus
        server/api/admin/debug/fix-embedding.post.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, readBody, useDB, where, from, db.select, eq, computeContentHash, stripMarkdown, onConflictDoUpdate, values, db.insert, set, db.update, isEmbeddingEnabled, limit, generatePostEmbedding, console.error
        server/api/admin/help/articles/[id].get.ts ↗
        • L1 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgMember, findHelpArticleDetail, getRouterParam, createError
        server/api/admin/help/articles/[id].patch.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, getRouterParam, readValidatedBody, useDB, and, eq, limit, where, from, db.select, createError, nextHelpArticlePosition, buildHelpTsv, generateHelpSlug, title.trim, content.trim, Object.keys, set, db.update, findHelpArticleDetail
        server/api/admin/help/articles/[id].delete.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, getRouterParam, useDB, returning, where, db.delete, and, eq, createError, setResponseStatus
        server/api/admin/help/articles/bulk-ai-visibility.post.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, readValidatedBody, returning, where, set, update, useDB, and, eq, inArray, ne
        server/api/admin/help/articles/bulk-publish.post.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, readValidatedBody, useDB, returning, where, set, db.update, and, eq, inArray, ne
        server/api/admin/help/articles/bulk-unpublish.post.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, readValidatedBody, useDB, returning, where, set, db.update, and, eq, inArray
        server/api/admin/help/articles/index.post.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, readValidatedBody, useDB, limit, where, from, db.select, and, eq, createError, nextHelpArticlePosition, withHelpShortId, returning, values, db.insert, generateHelpSlug, buildHelpTsv, setResponseStatus, findHelpArticleDetail
        server/api/admin/help/articles/index.get.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgMember, getQuery, Math.max, Number, Math.min, useDB, and, eq, ilike, where, from, db.select, offset, limit, orderBy, innerJoin, asc, rows.map
        server/api/admin/help/articles/reorder.patch.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, readValidatedBody, useDB, Promise.all, body.ids.map, where, set, db.update, and, eq, setResponseStatus
        server/api/admin/help/collections/[id].delete.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, getRouterParam, useDB, db.transaction, returning, where, tx.delete, and, eq, createError, setResponseStatus
        server/api/admin/help/collections/[id].patch.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, getRouterParam, readValidatedBody, useDB, and, eq, returning, where, set, db.update, createError, orderBy, from, db.select, asc
        server/api/admin/help/collections/index.get.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgMember, getQuery, useDB, orderBy, where, from, db.select, eq, asc, and, inArray, collections.map, articles.filter, own.map
        server/api/admin/help/collections/index.post.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, readValidatedBody, useDB, where, from, db.select, eq, returning, values, db.insert, setResponseStatus
        server/api/admin/help/stats.get.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgMember, useDB, where, from, db.select, eq
        server/api/admin/help/collections/reorder.patch.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, readValidatedBody, useDB, Promise.all, body.ids.map, where, set, db.update, and, eq, setResponseStatus
        server/api/admin/posts/[id].delete.ts ↗
        • L7 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, getRouterParam, useDB, limit, where, from, db.select, and, eq, createError, db.delete, returning, setResponseStatus
        server/api/admin/invitations/[id]/resend.post.ts ↗
        • L14 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, getRouterParam, useDB, limit, where, innerJoin, from, db.select, eq, and, createError, row.expiresAt.getTime, Date.now, buildInviteLink, sendEmail, renderInvitationEmail

        Environment references: process.env.RESEND_API_KEY

        server/api/admin/posts/[id].patch.ts ↗
        • L8 · Nuxt auto-imported H3 HTTP handler exported · calls getRouterParam, readValidatedBody, requireOrgPermission, useDB, limit, where, from, db.select, and, eq, createError, generateExcerpt, Object.keys, computeContentHash, returning, set, db.update, stripMarkdown, onConflictDoUpdate, values, db.insert, event.waitUntil, generatePostEmbedding
        server/api/admin/posts/[id]/notify-status.post.ts ↗
        • L15 · Nuxt auto-imported H3 HTTP handler exported · calls getRouterParam, readValidatedBody, requireOrgPermission, useDB, limit, where, from, db.select, and, eq, createError, catch, markPostUnreadForAuthor, console.error, resolveOrgBranding, resolvePostThreadRecipients, event.waitUntil, deliverToRecipients, getRequestURL
        server/api/admin/posts/authors.get.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgMember, useDB, orderBy, groupBy, where, leftJoin, from, db.select, eq, desc
        server/api/admin/posts/search.get.ts ↗
        • L11 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgMember, getQuery, trim, parsePostFilter, useDB, rows.map, embedQueryOrNull, searchPostsBySemantic, flat, and, postFilterConditions, limit, orderBy, where, leftJoin, innerJoin, from, db.select, eq, desc
        server/api/admin/posts/merge.post.ts ↗
        • L11 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, readValidatedBody, createError, useDB, limit, where, from, db.select, and, eq, db.transaction, set, tx.update, tx.execute, values, tx.insert
        server/api/admin/posts/index.get.ts ↗
        • L9 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgMember, getQuery, Math.max, Number, Math.min, useDB, postFilterConditions, parsePostFilter, requireOrgPermission, safeParse, z.uuid, createError, limit, where, from, db.select, and, eq, filters.push, offset, orderBy, leftJoin, orderFn, rows.map
        server/api/admin/posts/unmerge.post.ts ↗
        • L10 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, readValidatedBody, useDB, limit, where, from, db.select, and, eq, createError, db.transaction, set, tx.update, tx.execute, returning, tx.delete
        server/api/admin/widget/index.get.ts ↗
        • L8 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, limit, where, from, select, useDB, eq, resolveWidgetSettings, getRequestURL
        server/api/auth-config.get.ts ↗
        • L7 · Nuxt auto-imported H3 HTTP handler exported
        server/api/auth/[...all].ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls toWebRequest, guardSsoAuthRequest, auth.handler
        server/api/auth/claim-anonymous.post.ts ↗
        • L31 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, isGuestSession, createError, getRequestIP, checkRateLimit, readValidatedBody, auth.api.getSession, claimGuestContent
        server/api/admin/widget/index.patch.ts ↗
        • L10 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgPermission, updateWidgetSettingsSchema.safeParse, catch, readBody, createError, customRules.map, uuidv7, transaction, useDB, onConflictDoNothing, values, tx.insert, for, where, from, tx.select, eq, checkEnabledRuleCap, Object.keys, returning, set, tx.update, resolveWidgetSettings, getRequestURL
        server/api/auth/set-password.post.ts ↗
        • L3 · Nuxt auto-imported H3 HTTP handler exported · calls auth.api.getSession, getHeaders, createError, readBody, auth.api.listUserAccounts, auth.api.setPassword, sendEmail, renderPasswordSetEmail
        server/api/changelogs/[id]/reactions.delete.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, getRouterParam, getQuery, reactionSchema.safeParse, createError, useDB, limit, where, from, db.select, and, eq, isNotNull, db.delete, groupBy, userRows.map
        server/api/boards/index.get.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls useDB, Promise.all, orderBy, groupBy, where, leftJoin, from, db.select, and, eq, asc
        server/api/changelogs/[id]/reactions.post.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, assertGuestMay, getRouterParam, readValidatedBody, useDB, limit, where, from, db.select, and, eq, isNotNull, createError, onConflictDoNothing, values, db.insert, getReactionState
        • L39 · getReactionState calls (conditional paths may differ): groupBy, where, from, db.select, eq, and, userRows.map
        server/api/changelogs/[slug].get.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls getUserSession, getRouterParam, useDB, limit, where, from, db.select, and, eq, isNotNull, createError, groupBy, rows.map, toISOString
        server/api/changelogs/index.get.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls getUserSession, getQuery, Math.min, Number, useDB, eq, isNotNull, decodeCursor, conditions.push, and, limit, orderBy, where, from, db.select, desc, rows.slice, items.map, groupBy, inArray, push, toISOString, encodeCursor
        • L107 · encodeCursor calls (conditional paths may differ): toString, Buffer.from, JSON.stringify
        • L111 · decodeCursor calls (conditional paths may differ): JSON.parse, toString, Buffer.from
        server/api/comments/[id]/index.patch.ts ↗
        • L11 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, getRouterParam, useDB, limit, where, leftJoin, from, db.select, eq, and, createError, readValidatedBody, returning, set, db.update
        server/api/comments/[id]/index.delete.ts ↗
        • L7 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, getRouterParam, useDB, limit, where, leftJoin, from, db.select, eq, and, createError, requireOrgPermission, db.delete, set, db.update, setResponseStatus
        server/api/comments/[id]/like.delete.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, getRouterParam, useDB, limit, where, leftJoin, from, db.select, eq, and, createError, returning, db.delete, set, db.update
        server/api/developer/sso/secrets/[id].delete.ts ↗
        • L7 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgOwner, getRouterParam, createError, useDB, returning, where, db.delete, and, eq
        server/api/developer/sso/secrets/[id].patch.ts ↗
        • L7 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgOwner, getRouterParam, createError, readBody, slice, body.label.trim, Object.keys, useDB, returning, where, set, db.update, and, eq, ssoSecretToView
        server/api/comments/[id]/like.post.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, assertGuestMay, getRouterParam, useDB, limit, where, leftJoin, from, db.select, eq, and, createError, values, db.insert, returning, set, db.update
        server/api/developer/sso/secrets/index.get.ts ↗
        • L7 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgOwner, useDB, orderBy, where, from, db.select, eq, desc, rows.map
        server/api/developer/sso/secrets/index.post.ts ↗
        • L7 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgOwner, catch, readBody, useDB, db.$count, eq, createError, body.label.trim, slice, returning, values, db.insert, generateSsoSecret, setResponseStatus, ssoSecretToView
        server/api/files/[...path].get.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls getRouterParam, createError, blobStorage.serve
        server/api/help/articles/[shortId].get.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls requireHelpCenterOrg, split, getRouterParam, useDB, limit, where, innerJoin, from, db.select, eq, and, createError, orderBy, ne, asc
        server/api/help/collections/[id].get.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls requireHelpCenterOrg, getRouterParam, useDB, limit, where, from, db.select, and, eq, createError, orderBy, asc
        server/api/help/collections/index.get.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls requireHelpCenterOrg, useDB, orderBy, groupBy, where, innerJoin, from, db.select, and, eq, asc
        server/api/internal/scheduler/schedule.ts ↗
        • L8 · H3 HTTP handler exported · calls includes, createError, Object.keys, getQuery, readBody, Array.isArray, setResponseHeader, checkRateLimit, schedulerContext, withScheduledRuntime, scheduleInbox, delayTransport, restoreNodeTimers
        server/api/help/search.get.ts ↗
        • L7 · Nuxt auto-imported H3 HTTP handler exported · calls requireHelpCenterOrg, splitHelpQuery, trim, getQuery, buildHelpTsQuery, getRequestIP, checkRateLimit, createError, useDB, limit, orderBy, where, innerJoin, from, db.select, eq, and, asc, rows.map, buildHelpExcerpt, stripMarkdown, helpHighlightRanges
        server/api/me/preferences.get.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgMember, findOwnMemberPreference, useDB, createError, setResponseHeader, resolveMemberNotifications
        server/api/me/preferences.patch.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls requireOrgMember, updateMemberPreferenceSchema.safeParse, catch, readBody, createError, useDB, findOwnMemberPreference, saveMemberNotifications, setResponseHeader
        server/api/portal-identity.get.ts ↗
        • L18 · Nuxt auto-imported H3 HTTP handler exported · calls getOrgInfo, resolveLogoUrl, resolveBranding, resolveGuestAccess, resolvePortalModules
        • L9 · resolveLogoUrl calls (conditional paths may differ): logo.startsWith
        server/api/invitations/[id].get.ts ↗
        • L23 · Nuxt auto-imported H3 HTTP handler exported · calls getRouterParam, createError, limit, where, innerJoin, from, db.select, eq, row.expiresAt.toISOString
        server/api/posts/[postId]/comments/index.post.ts ↗
        • L16 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, getRouterParam, readValidatedBody, useDB, limit, where, from, db.select, and, eq, createError, assertGuestMay, returning, values, db.insert, set, db.update, event.waitUntil, catch, emitCommentNotifications, isActorAdmin, getRequestURL, console.error, emitAdminNotification, setResponseStatus
        server/api/posts/[postId]/comments/index.get.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls getUserSession, getRouterParam, getQuery, Math.min, Number, useDB, limit, where, from, db.select, and, eq, createError, resolveAdminAuthorIds, decodeCursor, conditions.push, orderBy, leftJoin, asc, rows.slice, attachLikeStatus, encodeCursor, toISOString, isNull, desc, topRows.slice, topItems.map, isNotNull, inArray, childrenMap.has, childrenMap.set, childrenMap.get, arr.push, children.splice, topItems.filter, filter, mergedPostItems.map, Promise.all, mergedPostInfoMap.set, groupedSubComments.has, groupedSubComments.set, push, groupedSubComments.get, level2MergedPostIds.push, level2PostInfoMap.set, allSubComments.slice, subData.map, subItems.find, level2PostInfoMap.get, mergedPostCommentsMap.set, topData.map, topItems.find, mergedPostInfoMap.get, mergedPostCommentsMap.get
        • L358 · resolveAdminAuthorIds calls (conditional paths may differ): where, from, select, useDB, and, eq, inArray, rows.map
        • L367 · formatComment calls (conditional paths may differ): adminIds.has
        • L384 · attachLikeStatus calls (conditional paths may differ): items.map, formatComment, where, from, db.select, and, eq, inArray, likes.map, likedSet.has
        • L405 · encodeCursor calls (conditional paths may differ): toString, Buffer.from, JSON.stringify
        • L409 · decodeCursor calls (conditional paths may differ): JSON.parse, toString, Buffer.from
        server/api/posts/[postId]/index.get.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls getUserSession, getRouterParam, useDB, limit, where, leftJoin, from, db.select, eq, and, createError, db.execute, getOrgMemberRole, setResponseHeader
        server/api/posts/[postId]/index.patch.ts ↗
        • L13 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, getRouterParam, useDB, limit, where, from, db.select, and, eq, createError, requireOrgPermission, readValidatedBody, generateExcerpt, Object.keys, computeContentHash, returning, set, db.update, stripMarkdown, onConflictDoUpdate, values, db.insert, event.waitUntil, generatePostEmbedding
        server/api/posts/[postId]/similar.get.ts ↗
        • L2 · Nuxt auto-imported H3 HTTP handler exported · calls getUserSession, getRouterParam, getQuery, Math.min, Math.max, Number, searchSimilarByPostId
        server/api/posts/[postId]/subscription.delete.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, getRouterParam, useDB, limit, where, from, db.select, and, eq, createError, db.delete
        server/api/posts/[postId]/vote.delete.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, getRouterParam, useDB, limit, where, from, db.select, and, eq, createError, returning, db.delete, set, db.update
        server/api/posts/[postId]/subscription.post.ts ↗
        • L8 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, getRouterParam, useDB, limit, where, from, db.select, and, eq, createError, isActorAdmin, onConflictDoNothing, values, db.insert
        server/api/posts/[postId]/vote.post.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, assertGuestMay, getRouterParam, useDB, limit, where, from, db.select, and, eq, createError, values, db.insert, returning, set, db.update, isActorAdmin, onConflictDoNothing
        server/api/posts/index.get.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls getUserSession, getQuery, Math.min, Number, useDB, eq, isNull, conditions.push, decodeCursor, and, limit, orderBy, where, leftJoin, from, db.select, orderFn, result.map, inArray, votes.map, result.slice, items.map, votedPostIds.has, encodeCursor
        • L110 · encodeCursor calls (conditional paths may differ): toString, Buffer.from, JSON.stringify
        • L114 · decodeCursor calls (conditional paths may differ): JSON.parse, toString, Buffer.from
        server/api/posts/index.post.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, assertGuestMay, readValidatedBody, createPostRecord, isActorAdmin, publishDomainEvent, createDomainEvent, fetchPostAuthor, setResponseStatus
        server/api/posts/search.get.ts ↗
        • L15 · Nuxt auto-imported H3 HTTP handler exported · calls getUserSession, getQuery, trim, useDB, getRequestIP, checkRateLimit, embedQueryOrNull, searchPostsBySemantic, limit, orderBy, where, leftJoin, innerJoin, from, db.select, eq, and, isNull, rows.map, inArray, votes.map, votedPostIds.has
        server/api/posts/similar.post.ts ↗
        • L16 · Nuxt auto-imported H3 HTTP handler exported · calls getUserSession, readValidatedBody, searchSimilarByText
        server/api/profile/avatar.post.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls requireLocalSession, createError, Number, getHeader, tooLarge, getRequestWebStream, reader.read, reader.cancel, chunks.push, reader.releaseLock, bytes.set, inspectAvatar, useRuntimeConfig, blobStorage.put, encodeURIComponent

        Environment references: process.env.UPLOAD_PREFIX · process.env.NUXT_PUBLIC_UPLOAD_PREFIX

        server/api/roadmap.get.ts ↗
        • L16 · Nuxt auto-imported H3 HTTP handler exported · calls getUserSession, getQuery, useDB, eq, ROADMAP_STATUSES.map, isNull, conditions.push, limit, orderBy, where, leftJoin, from, db.select, and, desc, inArray, countConditions.push, Promise.all, groupBy, counts.map, results.flat, allRows.map, votes.map, rows.slice, items.map, votedPostIds.has, encodeCursor, countMap.get
        • L128 · encodeCursor calls (conditional paths may differ): toString, Buffer.from, JSON.stringify
        server/api/sso/jwt.get.ts ↗
        • L12 · Nuxt auto-imported H3 HTTP handler exported · calls getQuery, validateReturnTo, createError, useDB, where, from, db.select, and, eq, verifySsoJwt, secrets.map, findOrCreateSsoUser, ctx.internalAdapter.createSession, signSessionCookieValue, rawSameSite.toLowerCase, setCookie, dataRawSameSite.toLowerCase, Object.keys, parseCookies, name.startsWith, deleteCookie, sendRedirect, console.warn, encodeURIComponent
        server/api/upload.post.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuth, useRuntimeConfig, blobStorage.handleUpload, createError

        Environment references: process.env.UPLOAD_PREFIX · process.env.NUXT_PUBLIC_UPLOAD_PREFIX

        server/api/widget/auth/exchange.post.ts ↗
        • L14 · Nuxt auto-imported H3 HTTP handler exported · calls createError, getRequestIP, checkRateLimit, catch, readBody, body.jwt.trim, useDB, limit, where, from, db.select, eq, and, verifySsoJwt, secrets.map, findOrCreateSsoUser, ctx.internalAdapter.createSession, toISOString
        server/api/widget/config.get.ts ↗
        • L11 · Nuxt auto-imported H3 HTTP handler exported · calls getOrgInfo, limit, where, from, select, useDB, eq, resolveBranding, setResponseHeader, resolveGuestAccess, pickBrandForegroundHex
        server/api/widget/conversations/[id]/chat.post.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls withAgentRuntime, streamAgentReply, widgetActor
        server/api/widget/conversations/[id]/items.get.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls withAgentRuntime, readConversationItems, widgetActor
        server/api/widget/conversations/[id]/messages.get.ts ↗
        • L16 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, getRouterParam, isConversationId, createError, useDB, limit, where, from, db.select, and, ownedConversation, withinRetention, orderBy, leftJoin, eq, asc, rows.map
        server/api/widget/conversations/[id]/read.post.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls withAgentRuntime, widgetActor, safeParse, z.uuid, getRouterParam, agentError, strict, z.object, safe, positive, int, z.number, catch, readBody, markCustomerRead, agentRuntime, countWidgetBadge
        server/api/widget/conversations/index.get.ts ↗
        • L19 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, orderBy, where, from, select, useDB, and, eq, withinRetention, gt, desc, rows.map, handling
        server/api/widget/feedback/[postId]/read.post.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, getRouterParam, createError, useDB, limit, where, from, db.select, and, eq, db.delete, countWidgetBadge
        server/api/widget/feedback/index.get.ts ↗
        • L19 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, getQuery, Math.min, Number, eq, decodeCursor, conditions.push, limit, orderBy, where, leftJoin, from, select, useDB, and, desc, map, rows.slice, encodeCursor, count
        • L81 · encodeCursor calls (conditional paths may differ): toString, Buffer.from, JSON.stringify
        • L91 · decodeCursor calls (conditional paths may differ): JSON.parse, toString, Buffer.from, Number.isNaN, Date.parse, UUID.test
        server/api/widget/messages.post.ts ↗
        • L1 · Nuxt auto-imported H3 HTTP handler exported · calls createError
        server/api/widget/unread.get.ts ↗
        • L5 · Nuxt auto-imported H3 HTTP handler exported · calls requireAuthInOrg, countWidgetBadge
        server/db/index.ts ↗
        • L12 · getDb calls (conditional paths may differ): drizzle, postgres, logger.info

        Environment references: process.env.DATABASE_URL

        server/lib/agent/agent.ts ↗
        • L10 · historyProcessor calls (conditional paths may differ): messages.filter, customerItemIds.has, completedRunIds.has, messages.map
        • L27 · customerMemoryMessage calls (conditional paths may differ): parts.push, useRuntimeConfig, part.storage_key.startsWith, includes, part.storage_key.split, blobStorage.get, blob.type.startsWith, blob.arrayBuffer, JSON.stringify, toString, Buffer.from, padStart, String
        • L46 · prepareAgent calls (conditional paths may differ): agentRuntime, memory.getThreadById, memory.createThread, pool.query, Promise.all, map, customerItems.filter, customerMemoryMessage, memory.saveMessages, historyProcessor, customerItems.map, completed.map, loadAgentPromptContext, JSON.stringify, feedlogTools, renderAgentSystemPrompt

        Environment references: process.env.OPENAI_API_KEY · process.env.OPENAI_TEXT_MODEL · process.env.OPENAI_BASE_URL

        server/lib/agent/conversations.ts ↗
        • L9 · startRun calls (conditional paths may differ): catch, transaction, client.query, lockConversation, Number, agentError, isDeepStrictEqual, handling, recoverLocked, eventItem, appendItem, syncConversationTask, uuidv7, syncRunTask
        • L71 · failRun calls (conditional paths may differ): transaction, client.query, syncRunTask
        server/lib/agent/help-search.ts ↗
        • L32 · searchHelpArticles calls (conditional paths may differ): helpArticleSearchInput.parse, pool.query, resolvePortalModules, query.replace, normalized.toLowerCase, seen.has, seen.add, queries.push, Promise.all, queries.map, groups.entries, articles.get, existing.matched_queries.push, buildHelpExcerpt, stripMarkdown, splitHelpQuery, articles.set, articles.values
        server/lib/agent/chat.ts ↗
        • L17 · streamAgentReply calls (conditional paths may differ): assertGuestMay, safeParse, z.uuid, getRouterParam, agentError, chatInput.safeParse, catch, readBody, agentRuntime, customerMemoryMessage, checkRateLimit, startRun, requestSchedule, publicItem, publicRun, Number, handling, uuidv7, createEventStream, stream.push
        server/lib/agent/history.ts ↗
        • L7 · readConversationItems calls (conditional paths may differ): safeParse, z.uuid, getRouterParam, agentError, pageInput.safeParse, getQuery, agentRuntime, runtime.pool.query, transaction, client.query, rows.slice, items.reverse, map, items.filter, Number, handling, filter, items.map, runs.map, items.at
        server/lib/agent/prompts/context.ts ↗
        • L9 · agentProduct calls (conditional paths may differ): pool.query
        • L17 · loadAgentPromptContext calls (conditional paths may differ): Promise.all, pool.query, resolveGuestAccess, isGuestSession, agentProduct, getEnabledSupportRules, isActorAdmin
        server/lib/agent/prompts/system.ts ↗
        • L30 · renderAgentSystemPrompt calls (conditional paths may differ): context.supportRules.filter, rule.scenario.trim, join, filter, context.boards.map, board.description.trim, JSON.stringify, context.supportEmail.trim, rules.map, context.articles.map, sections.map, section.filter
        • L133 · renderAgentPromptTemplate calls (conditional paths may differ): renderAgentSystemPrompt
        server/lib/agent/tools.ts ↗
        • L24 · feedlogTools calls (conditional paths may differ): agentRuntime, digest, update, createHash, cards.findIndex, cards.push, signal.throwIfAborted, lockConversation, client.query, memory.getThreadById, effects, uuidv7, memory.updateThread, map, filter, Object.values, has, createdIds, ids.has, getRequestURL
        • L35 · active calls (conditional paths may differ): signal.throwIfAborted, lockConversation, client.query
        • L42 · effects calls (conditional paths may differ): memory.getThreadById
        • L46 · reserve calls (conditional paths may differ): effects, uuidv7, memory.updateThread
        • L55 · createdIds calls (conditional paths may differ): effects, map, filter, Object.values
        • L59 · owned calls (conditional paths may differ): has, createdIds
        • L62 · feedbackEditable calls (conditional paths may differ): ids.has
        • L65 · feedbackDetails calls (conditional paths may differ): getRequestURL, feedbackEditable
        • L70 · postRow calls (conditional paths may differ): client.query
        • L84 · tool calls (conditional paths may differ): createTool, signal.throwIfAborted, uuidv7, Date.now, emit, execute
        • L102 · write calls (conditional paths may differ): transaction, active, fn
        • L110 · attachImages calls (conditional paths may differ): pool.query, rows.map, images.some, allowed.has, join, map
        • L268 · resultParts calls (conditional paths may differ): pool.query, resolvePortalModules, visible.map, Promise.all, map, cards.flatMap, postRow, cards.map, card, posts.get, refreshed.flatMap, part.articles.some, ids.has, part.articles.filter, cards.some
        • L289 · feedbackResults calls (conditional paths may differ): resultParts, console.warn, parts.filter
        server/lib/agent/stream.ts ↗
        • L4 · collectConversationReply calls (conditional paths may differ): stream.pipeThrough, write, controller.enqueue, readUIMessageStream
        server/lib/agent/runtime.ts ↗
        • L12 · initialize calls (conditional paths may differ): memory.settled, Promise.all, pool.end, memoryPool.end
        • L27 · agentRuntime calls (conditional paths may differ): initialize, requestRuntime
        • L40 · requestRuntime calls (conditional paths may differ): publishDomainEvent, createDomainEvent
        • L44 · withAgentRuntime calls (conditional paths may differ): action, event.waitUntil, finally, Promise.resolve, runtime.close
        • L54 · agentError calls (conditional paths may differ): createError
        • L57 · widgetActor calls (conditional paths may differ): requireAuthInOrg, agentRuntime, pool.query, agentError
        • L66 · transaction calls (conditional paths may differ): runtime.pool.connect, client.query, fn, takeInboxEvents, console.warn, client.release
        • L80 · lockConversation calls (conditional paths may differ): client.query, agentError
        • L86 · appendItem calls (conditional paths may differ): slice, stripMarkdown, join, input.content.parts.map, client.query, JSON.stringify
        • L98 · publicItem calls (conditional paths may differ): parts.find, Number

        Environment references: process.env.DATABASE_URL

        server/lib/inbox/email-context.ts ↗
        • L15 · safeEmailTopic calls (conditional paths may differ): value.trim, Object.hasOwn
        • L20 · sourcePageUrl calls (conditional paths may differ): pageContext.safeParse
        • L28 · customerQuestionText calls (conditional paths may differ): slice, join, map, messages.filter, message.content.parts.filter
        • L34 · summarizeEmailQuestion calls (conditional paths may differ): customerQuestionText, input.trim, join, Object.keys, agent.generate, AbortSignal.timeout, safeEmailTopic

        Environment references: process.env.OPENAI_API_KEY · process.env.OPENAI_TEXT_MODEL · process.env.OPENAI_BASE_URL

        server/lib/inbox/attachments.ts ↗
        • L3 · assertConversationImage calls (conditional paths may differ): key.startsWith, some, key.split, agentError
        server/lib/inbox/email.ts ↗
        • L10 · sendInboxEmail calls (conditional paths may differ): runtime.pool.query, agentError, resolveEmailProvider, find, customerMessages.map, sourcePageUrl, summarizeEmailQuestion, reverse, join, parts.map, sendEmail, renderInboxReplyEmail, console.warn
        • L60 · deliverReplyNow calls (conditional paths may differ): runtime.pool.query, deliverReplyTask
        • L78 · deliverReplyTask calls (conditional paths may differ): runtime.pool.query, finish, BigInt, sendInboxEmail, setTimeout
        • L99 · retryReplyEmail calls (conditional paths may differ): runtime.pool.query, agentError
        server/lib/inbox/http.ts ↗
        • L6 · parseInbox calls (conditional paths may differ): schema.safeParse, agentError
        • L11 · inboxRoute calls (conditional paths may differ): defineEventHandler, withAgentRuntime, requireOrgPermission, getRouterParam, parseInbox, z.uuid, setResponseHeader, handler, agentRuntime, requestSchedule
        server/lib/inbox/platform.ts ↗
        • L18 · consumeBackground calls (conditional paths may differ): withScheduledRuntime, publications.push, publishBackgroundDomainEvent, createDomainEvent, consumeInbox, delayTransport, Promise.allSettled
        • L28 · arm calls (conditional paths may differ): timers.get, clearTimeout, setTimeout, timers.delete, consumeBackground, console.warn, schedulerFailure, arm, Math.max, Math.min, timers.set
        • L43 · stopInboxTimers calls (conditional paths may differ): timers.values, clearTimeout, timers.clear
        • L48 · delayTransport calls (conditional paths may differ): queue.send, send, stopInboxTimers, arm, Math.max
        • L69 · requestSchedule calls (conditional paths may differ): schedulerContext, localFetch, useNitroApp, Object.assign, response.json, console.warn, schedulerFailure
        • L85 · restoreNodeTimers calls (conditional paths may differ): withScheduledRuntime, runtime.pool.query, arm, toISOString, Date.now, requestSchedule

        Environment references: context.cloudflare.env.INBOX_QUEUE · process.env.VERCEL

        server/lib/inbox/events.ts ↗
        • L11 · queueInboxEvent calls (conditional paths may differ): pending.set, pending.get
        • L14 · takeInboxEvents calls (conditional paths may differ): pending.get, pending.delete
        server/lib/inbox/queries.ts ↗
        • L7 · listConversations calls (conditional paths may differ): digest, update, createHash, JSON.stringify, values.push, conditions.push, bind, input.q.replace, parse, z.tuple, z.iso.datetime, z.uuid, z.literal, JSON.parse, toString, Buffer.from, agentError, runtime.pool.query, conditions.join, rows.slice
        • L58 · readInbox calls (conditional paths may differ): transaction, client.query, agentError, rows.slice, items.reverse, Object.fromEntries, emailTasks.map, conversationResult, items.map, Number, items.at
        server/lib/inbox/read.ts ↗
        • L3 · markCustomerRead calls (conditional paths may differ): runtime.pool.query
        server/lib/inbox/scheduler.ts ↗
        • L20 · schedulerFailure calls (conditional paths may differ): test, includes
        • L27 · wakeAt calls (conditional paths may differ): Math.min, Math.ceil
        • L33 · isQuotaExhausted calls (conditional paths may differ): String, test
        • L39 · pending calls (conditional paths may differ): client.query
        • L48 · notifyQuota calls (conditional paths may differ): resolveEmailProvider, client.query, sendEmail, console.warn
        • L65 · publish calls (conditional paths may differ): client.query, toISOString, transport.send, Math.max, Math.min, Math.ceil, Date.now, expectAt.toISOString, isQuotaExhausted, console.warn, schedulerFailure, none
        • L82 · locked calls (conditional paths may differ): runtime.pool.connect, client.query, fn, client.release
        • L96 · scheduleInbox calls (conditional paths may differ): locked, none, pending, client.query, wakeAt, uuidv7, publish, notifyQuota
        • L129 · consumeInbox calls (conditional paths may differ): test, locked, client.query, toISOString, includes, Date.now, inboxTick, scheduleClient.query, none, pending, publish, wakeAt, notifyQuota
        • L158 · withScheduledRuntime calls (conditional paths may differ): action, pool.end
        • L167 · runScheduledInbox calls (conditional paths may differ): withScheduledRuntime

        Environment references: context.cloudflare.env.POSTGRES · process.env.DATABASE_URL

        server/lib/inbox/tasks.ts ↗
        • L9 · syncConversationTask calls (conditional paths may differ): client.query, uuidv7
        • L17 · syncRunTask calls (conditional paths may differ): client.query, uuidv7
        • L24 · scheduleReplyEmail calls (conditional paths may differ): client.query, uuidv7
        server/lib/inbox/tick.ts ↗
        • L5 · inboxTick calls (conditional paths may differ): Date.now, runtime.pool.query, rows.slice, deliverReplyTask, transaction, client.query, includes, recoverLocked, eventItem, console.warn, console.info
        server/lib/inbox/service.ts ↗
        • L26 · conversationResult calls (conditional paths may differ): Number
        • L38 · lockInbox calls (conditional paths may differ): client.query, agentError
        • L48 · cancelRun calls (conditional paths may differ): client.query, syncRunTask
        • L56 · eventItem calls (conditional paths may differ): appendItem, uuidv7
        • L70 · handoffLocked calls (conditional paths may differ): client.query, appendItem, cancelRun, includes, syncRunTask, syncConversationTask, console.warn, queueInboxEvent, eventItem
        • L118 · handoff calls (conditional paths may differ): transaction, handoffLocked, lockInbox, save, console.warn
        • L139 · recoverLocked calls (conditional paths may differ): client.query, handoffLocked, cancelRun
        • L151 · canonical calls (conditional paths may differ): Array.isArray, join, value.map, map, sort, Object.entries, a.localeCompare, JSON.stringify, canonical
        • L161 · staffCommand calls (conditional paths may differ): hash, transaction, lockInbox, client.query, agentError, receipt, conversationResult, Number, includes, queueInboxEvent, appendItem, getTime, replyCloseDelayMs, scheduleReplyEmail, due.getTime, due.toISOString, cancelRun, eventItem, syncConversationTask
        • L264 · receipt calls (conditional paths may differ): Number
        server/lib/inbox/unread.ts ↗
        • L4 · inboxOpenActivity calls (conditional paths may differ): runtime.pool.query, rows.map, Number
        server/middleware/00-widget-cors.ts ↗
        • L4 · Nuxt auto-imported H3 HTTP handler exported · calls isWidgetApiPath, applyWidgetCors, handleWidgetPreflight
        server/middleware/01-frame-ancestors.ts ↗
        • L14 · Nuxt auto-imported H3 HTTP handler exported · calls event.path.split, path.startsWith, setResponseHeader
        server/middleware/org-context.ts ↗
        • L6 · Nuxt auto-imported H3 HTTP handler exported · calls resolveOrgContext
        server/plugins/email.ts ↗

          Environment references: process.env.RESEND_API_KEY · process.env.EMAIL_FROM

          server/middleware/inbox-queue.ts ↗
          • L7 · H3 HTTP handler exported · calls getHeader, handleCallback, consumeBackground, handler, toWebRequest

          Environment references: process.env.VERCEL

          server/plugins/feedback-listeners.ts ↗
          • L11 · loadFeedbackPost calls (conditional paths may differ): limit, where, from, select, useDB, eq
          • L29 · actorIsOrgAdmin calls (conditional paths may differ): limit, where, from, select, useDB, and, eq
          server/plugins/migrate.ts ↗

            Environment references: process.env.DATABASE_URL · process.env.MIGRATIONS_DIR

            server/plugins/zz-inbox-scheduler.ts ↗

              Environment references: process.env.VERCEL

              server/routes/health.get.ts ↗
              • L1 · Nuxt auto-imported H3 HTTP handler exported · calls toISOString
              server/services/member-preferences.ts ↗
              • L9 · findOwnMemberPreference calls (conditional paths may differ): limit, where, leftJoin, innerJoin, from, db.select, eq, and
              • L25 · saveMemberNotifications calls (conditional paths may differ): returning, onConflictDoUpdate, values, db.insert, JSON.stringify, resolveMemberNotifications
              • L39 · findStaffFeedbackRecipients calls (conditional paths may differ): where, leftJoin, innerJoin, from, db.select, eq, and, inArray, ne, map, rows.filter, resolveMemberNotifications
              server/utils/auth.ts ↗
              • L13 · getUserSession calls (conditional paths may differ): auth.api.getSession
              • L29 · requireAuth calls (conditional paths may differ): getUserSession, createError
              • L41 · assertNotGuestSession calls (conditional paths may differ): createError
              • L51 · requireLocalSession calls (conditional paths may differ): requireAuth, createError
              • L74 · requireOrgPermission calls (conditional paths may differ): requireAuth, assertNotGuestSession, createError, orgApi.hasPermission
              • L109 · requireAuthInOrg calls (conditional paths may differ): requireAuth, createError
              • L120 · requireOrgMember calls (conditional paths may differ): requireAuth, assertNotGuestSession, createError
              • L151 · requireOrgOwner calls (conditional paths may differ): requireOrgMember, createError
              server/utils/better-auth.ts ↗
              • L27 · parseBool calls (conditional paths may differ): includes, v.toLowerCase
              • L103 · loadOrgList calls (conditional paths may differ): where, innerJoin, from, db.select, eq, and
              • L127 · bootstrapAfterUserCreate calls (conditional paths may differ): systemAdminEmails.includes, onConflictDoNothing, values, db.insert, uuidv7
              • L141 · nameGuestUser calls (conditional paths may differ): where, set, db.update, guestTag, eq
              • L167 · buildAuthConfig calls (conditional paths may differ): sendEmail, renderInvitationEmail, rawSendInvite, logger.warn, defu, nameGuestUser, bootstrap, drizzleAdapter, bearer, admin, anonymous, uuidv7, organization, customSession, loadOrgList
              • L285 · getAuthInstance calls (conditional paths may differ): betterAuth, buildAuthConfig

              Environment references: env.SYSTEM_ADMIN_EMAILS · env.GOOGLE_CLIENT_ID · env.GOOGLE_CLIENT_SECRET · env.GITHUB_CLIENT_ID · env.GITHUB_CLIENT_SECRET · env.RESEND_API_KEY · env.AUTH_EMAIL_ENABLED · env.AUTH_EMAIL_VERIFY · env.BETTER_AUTH_URL

              server/utils/blob.ts ↗
              • L20 · registerBlobProvider calls (conditional paths may differ): providers.set
              • L24 · resolveBlobProvider calls (conditional paths may differ): useRuntimeConfig, providers.has, providers.get
              server/utils/changelog-ai.ts ↗
              • L111 · buildSystemPrompt calls (conditional paths may differ): SYSTEM_PROMPT_TEMPLATE.replace
              • L115 · buildUserPrompt calls (conditional paths may differ): parts.push, feedback.forEach, changeContent.trim, parts.join
              • L155 · parseAiResponse calls (conditional paths may differ): JSON.parse, validateAiOutput, raw.match, raw.indexOf, raw.lastIndexOf, raw.slice, createError
              • L185 · validateAiOutput calls (conditional paths may differ): Array.isArray, o.suggested_tags.every
              server/utils/conversation.ts ↗
              • L8 · isConversationId calls (conditional paths may differ): UUID.test
              • L12 · ownedConversation calls (conditional paths may differ): and, eq
              • L24 · estimateTokens calls (conditional paths may differ): text.match, Math.ceil
              • L29 · withinRetention calls (conditional paths may differ): gt
              server/utils/email-templates.ts ↗
              • L9 · renderInboxReplyEmail calls (conditional paths may differ): join, filter, layout, escapeHtml, actionButton
              • L16 · layout calls (conditional paths may differ): escapeHtml, signature
              • L33 · fallbackLink calls (conditional paths may differ): url.slice
              • L60 · renderVerificationEmail calls (conditional paths may differ): layout, escapeHtml, actionButton, fallbackLink, expiryNote
              • L79 · renderInvitationEmail calls (conditional paths may differ): layout, escapeHtml, actionButton, fallbackLink, expiryNote
              • L92 · renderResetPasswordEmail calls (conditional paths may differ): layout, escapeHtml, actionButton, fallbackLink, expiryNote
              • L110 · renderPasswordSetEmail calls (conditional paths may differ): layout, escapeHtml
              • L127 · escapeHtml calls (conditional paths may differ): replace, s.replace
              • L141 · notificationShell calls (conditional paths may differ): escapeHtml
              • L164 · statusChangeCard calls (conditional paths may differ): escapeHtml, pillButton
              • L182 · personCard calls (conditional paths may differ): toUpperCase, charAt, test, escapeHtml, pillButton
              • L201 · adminReplyCard calls (conditional paths may differ): personCard
              • L209 · newFeedbackCard calls (conditional paths may differ): personCard
              • L217 · userCommentCard calls (conditional paths may differ): personCard
              • L227 · renderNotificationEmail calls (conditional paths may differ): statusChangeCard, adminReplyCard, newFeedbackCard, userCommentCard, normalizeBrandHex, replaceAll, notificationShell, card.html.replaceAll, escapeHtml, pickBrandForegroundHex
              server/utils/domain-events.ts ↗
              • L140 · toHookName calls (conditional paths may differ): name.replace
              • L156 · createDomainEvent calls (conditional paths may differ): uuidv7, toISOString
              • L169 · publishDomainEvent calls (conditional paths may differ): getRequestURL, event.waitUntil, dispatchDomainEvent, useNitroApp
              • L183 · publishBackgroundDomainEvent calls (conditional paths may differ): dispatchDomainEvent, useNitroApp
              • L194 · dispatchDomainEvent calls (conditional paths may differ): hooks.callHookWith, Promise.allSettled, handlers.map, handler, console.error, errorSummary, toHookName
              • L229 · errorSummary calls (conditional paths may differ): String
              • L236 · onDomainEvent calls (conditional paths may differ): nitroApp.hooks.hook, toHookName
              • L248 · onAnyDomainEvent calls (conditional paths may differ): nitroApp.hooks.hook, toHookName
              server/utils/email.ts ↗
              • L21 · registerEmailProvider calls (conditional paths may differ): providers.set
              • L25 · resolveEmailProvider calls (conditional paths may differ): providers.has, providers.get
              • L40 · sendEmail calls (conditional paths may differ): options.subject.replace, send, resolveEmailProvider

              Environment references: process.env.EMAIL_PROVIDER

              server/utils/embedQuery.ts ↗
              • L5 · embedQueryOrNull calls (conditional paths may differ): isEmbeddingEnabled, generateEmbedding
              server/utils/embedding.ts ↗
              • L11 · generateEmbedding calls (conditional paths may differ): replace, fetch, JSON.stringify, response.text, response.json
              • L39 · generatePostEmbedding calls (conditional paths may differ): isEmbeddingEnabled, stripMarkdown, generateEmbedding, useDB, limit, where, from, db.select, eq, set, db.update, and, onConflictDoNothing, values, db.insert, console.error

              Environment references: process.env.OPENAI_API_KEY · process.env.OPENAI_BASE_URL

              server/utils/guest-claim.ts ↗
              • L34 · claimGuestContent calls (conditional paths may differ): transaction, useDB, alias, returning, where, tx.delete, and, eq, exists, from, tx.select, set, tx.update, inArray, droppedVotes.map, droppedLikes.map
              server/utils/guest.ts ↗
              • L15 · getGuestAccess calls (conditional paths may differ): getOrgInfo, resolveGuestAccess
              • L32 · assertGuestMay calls (conditional paths may differ): isGuestSession, getGuestAccess, createError
              server/utils/help-portal.ts ↗
              • L5 · requireHelpCenterOrg calls (conditional paths may differ): getOrgInfo, resolvePortalModules, createError
              server/utils/hash.ts ↗
              • L3 · computeContentHash calls (conditional paths may differ): digest, update, createHash
              server/utils/help-search.ts ↗
              • L18 · splitHelpQuery calls (conditional paths may differ): slice, filter, split, q.trim, tokens.push, CJK_CHAR.test, flush, tokens.filter, test
              • L44 · buildHelpTsQuery calls (conditional paths may differ): tokens.map, join, split, cjkBigrams, parts.reduce
              • L55 · escapeRegExp calls (conditional paths may differ): text.replace
              • L59 · mergeRanges calls (conditional paths may differ): ranges.sort, Math.max, out.push
              • L72 · helpHighlightRanges calls (conditional paths may differ): token.text.replace, escapeRegExp, text.matchAll, ranges.push, mergeRanges
              • L97 · truncateToWidth calls (conditional paths may differ): charWidth, text.slice
              • L106 · buildHelpExcerpt calls (conditional paths may differ): helpHighlightRanges, truncateToWidth, trim, charWidth, body.slice, map, hits.filter
              server/utils/invite.ts ↗
              • L23 · buildInviteLink calls (conditional paths may differ): _builder
              server/utils/org-cache.ts ↗
              • L44 · getOrgInfo calls (conditional paths may differ): _backend.get
              • L48 · invalidateOrgInfo calls (conditional paths may differ): _backend.invalidate
              server/utils/notifications.ts ↗
              • L45 · resolvePostThreadRecipients calls (conditional paths may differ): useDB, db.execute
              • L76 · resolveOrgBranding calls (conditional paths may differ): useDB, limit, where, from, db.select, eq, resolveBranding
              • L83 · deliverToRecipients calls (conditional paths may differ): sendNotification
              • L108 · emitAdminReply calls (conditional paths may differ): useDB, resolveOrgBranding, console.error, resolvePostThreadRecipients, limit, where, from, db.select, eq, deliverToRecipients, input.snippet.slice
              • L128 · emitCommentNotifications calls (conditional paths may differ): resolveCommentEvents, catch, markPostUnreadForAuthor, console.error, emitAdminReply
              • L151 · resolveOrgAdminRecipients calls (conditional paths may differ): findStaffFeedbackRecipients, useDB
              • L155 · emitAdminNotification calls (conditional paths may differ): useDB, resolveOrgBranding, console.error, resolveOrgAdminRecipients, limit, where, from, db.select, eq, input.snippet.slice, sendNotification
              server/utils/notification-send.ts ↗
              • L27 · buildNotificationEmail calls (conditional paths may differ): buildPostLink, renderNotificationEmail
              • L48 · sendNotification calls (conditional paths may differ): buildNotificationEmail, console.error, sendEmail
              server/utils/help.ts ↗
              • L13 · generateHelpShortId calls (conditional paths may differ): shortId
              • L17 · generateHelpSlug calls (conditional paths may differ): join, map, pinyin, slice, slugify
              • L25 · cjkBigrams calls (conditional paths may differ): CJK.test, out.push, text.slice, out.join
              • L33 · buildHelpTsv calls (conditional paths may differ): stripMarkdown, cjkBigrams
              • L43 · withHelpShortId calls (conditional paths may differ): insert, generateHelpShortId, createError
              • L55 · findHelpArticleDetail calls (conditional paths may differ): useDB, limit, where, innerJoin, from, db.select, eq, and
              • L86 · nextHelpArticlePosition calls (conditional paths may differ): useDB, where, from, db.select, eq
              server/utils/org-context.ts ↗
              • L27 · resolveOrgContext calls (conditional paths may differ): _resolver
              server/utils/password-hash.ts ↗
              • L17 · bytesToHex calls (conditional paths may differ): join, Array.from, padStart, b.toString
              • L21 · hexToBytes calls (conditional paths may differ): parseInt, hex.slice
              • L34 · deriveBits calls (conditional paths may differ): crypto.subtle.importKey, encode, password.normalize, crypto.subtle.deriveBits
              • L46 · hashPassword calls (conditional paths may differ): crypto.getRandomValues, deriveBits, bytesToHex
              • L52 · verifyPassword calls (conditional paths may differ): hash.split, parseInt, Number.isFinite, hexToBytes, deriveBits, constantTimeEqual
              server/utils/post-link-builder.ts ↗
              • L24 · buildPostLink calls (conditional paths may differ): _builder

              Environment references: process.env.BETTER_AUTH_URL

              server/utils/post-create.ts ↗
              • L38 · createPostRecord calls (conditional paths may differ): generateSlug, generateExcerpt, computeContentHash, useDB, returning, values, db.insert, createError, stripMarkdown, onConflictDoNothing
              • L77 · fetchPostAuthor calls (conditional paths may differ): where, from, select, useDB, eq
              server/utils/postSemanticSearch.ts ↗
              • L14 · searchPostsBySemantic calls (conditional paths may differ): useDB, embedding.join, postFilterConditions, conditions.push, limit, orderBy, where, leftJoin, innerJoin, from, db.select, eq, and
              server/utils/rateLimit.ts ↗
              • L4 · checkRateLimit calls (conditional paths may differ): useStorage, Math.floor, Date.now, storage.getItem, storage.setItem
              server/utils/postFilterConditions.ts ↗
              • L22 · list calls (conditional paths may differ): filter, map, v.split, s.trim
              • L28 · date calls (conditional paths may differ): Number.isNaN, d.getTime
              • L34 · parsePostFilter calls (conditional paths may differ): list, date
              • L55 · boardCondition calls (conditional paths may differ): f.boardId.filter, isNull, inArray, or, f.boardIdNot.filter, isNotNull, notInArray, and
              • L73 · postFilterConditions calls (conditional paths may differ): eq, conditions.push, isNotNull, isNull, inArray, notInArray, boardCondition, gte, lt
              server/utils/runtime-migrate.ts ↗
              • L29 · runRuntimeMigration calls (conditional paths may differ): Date.now, postgres, drizzle, db.execute, migrate, String, client.end
              server/utils/similar.ts ↗
              • L24 · searchSimilarByText calls (conditional paths may differ): stripMarkdown, isEmbeddingEnabled, generateEmbedding, searchSimilarByEmbedding, searchSimilarByTrgm
              • L40 · searchSimilarByEmbedding calls (conditional paths may differ): useDB, embedding.join, eq, isNull, conditions.push, ne, limit, orderBy, where, innerJoin, from, db.select, and, attachHasVoted
              • L76 · searchSimilarByTrgm calls (conditional paths may differ): useDB, eq, isNull, conditions.push, ne, limit, orderBy, where, innerJoin, from, db.select, and, attachHasVoted
              • L111 · searchSimilarByPostId calls (conditional paths may differ): useDB, isEmbeddingEnabled, limit, where, from, db.select, eq, searchSimilarByEmbedding, searchSimilarByTrgm
              • L145 · attachHasVoted calls (conditional paths may differ): rows.map, where, from, db.select, and, eq, votes.map, votedSet.has
              server/utils/slug.ts ↗
              • L10 · generateSlugWith calls (conditional paths may differ): nanoid, slugify, exists, createError
              • L23 · generateSlug calls (conditional paths may differ): useDB, generateSlugWith, limit, where, from, db.select, eq
              • L32 · generateChangelogSlug calls (conditional paths may differ): useDB, generateSlugWith, limit, where, from, db.select, eq
              server/utils/sso-auth-guard.ts ↗
              • L40 · localAuthRequired calls (conditional paths may differ): createError
              • L48 · assertTargetOrgIsSessionOrg calls (conditional paths may differ): createError, reject, getQuery, check, PAYLOAD_METHODS.has, json, request.clone
              • L87 · guardSsoAuthRequest calls (conditional paths may differ): path.startsWith, path.slice, rest.startsWith, rest.split, GLOBAL_IDENTITY_ENDPOINTS.has, auth.api.getSession, createError, localAuthRequired, split, rest.slice, ORG_SCOPED_ORGANIZATION_ENDPOINTS.has, assertTargetOrgIsSessionOrg
              server/utils/sso.ts ↗
              • L28 · verifySsoJwt calls (conditional paths may differ): jwtVerify, enc.encode, createError, Math.floor, Date.now, email.includes, toLowerCase, email.trim, payload.name.trim
              • L84 · findOrCreateSsoUser calls (conditional paths may differ): limit, where, from, db.select, eq, onConflictDoNothing, values, db.insert, uuidv7, createError
              • L116 · validateReturnTo calls (conditional paths may differ): returnTo.includes, returnTo.startsWith, getRequestURL
              • L132 · generateSsoSecret calls (conditional paths may differ): crypto.getRandomValues, padStart, toString
              • L150 · ssoSecretToView calls (conditional paths may differ): row.createdAt.toISOString
              • L170 · signSessionCookieValue calls (conditional paths may differ): crypto.subtle.importKey, enc.encode, crypto.subtle.sign, String.fromCharCode, btoa
              server/utils/widget-cors.ts ↗
              • L13 · isWidgetApiPath calls (conditional paths may differ): path.startsWith
              • L17 · applyWidgetCors calls (conditional paths may differ): getRequestHeader, setResponseHeader, appendResponseHeader
              • L35 · handleWidgetPreflight calls (conditional paths may differ): setResponseStatus
              server/utils/widget-unread.ts ↗
              • L9 · countWidgetBadge calls (conditional paths may differ): useDB, where, innerJoin, from, db.select, count, eq, and, gt, withinRetention, Number
              • L41 · markPostUnreadForAuthor calls (conditional paths may differ): useDB, limit, where, from, db.select, eq, onConflictDoNothing, values, db.insert
              shared/agent/sse.ts ↗
              • L11 · readAgentEvents calls (conditional paths may differ): stream.getReader, reader.read, decoder.decode, exec, buffer.slice, block.split, lines.find, line.startsWith, join, map, lines.filter, trimStart, line.slice, onEvent, JSON.parse, reader.releaseLock
              shared/agent/usage.ts ↗
              • L14 · addTokenUsage calls (conditional paths may differ): Number.isSafeInteger, count, add
              • L35 · summarizeTokenUsage calls (conditional paths may differ): runs.flatMap, values.reduce, sum, runs.filter, runs.every
              shared/schemas/member-preference.ts ↗
              • L16 · resolveMemberNotifications calls (conditional paths may differ): storedNotificationsSchema.parse
              shared/schemas/widget.ts ↗
              • L50 · checkEnabledRuleCap calls (conditional paths may differ): WIDGET_BUILTIN_RULE_IDS.filter, disabledBuiltins.includes, customRules.filter
              shared/utils/avatar.ts ↗
              • L16 · inspectAvatar calls (conditional paths may differ): imageMeta, includes, Number.isInteger
              shared/utils/branding.ts ↗
              • L34 · hexToLinear calls (conditional paths may differ): trim, hex.replace, lin, parseInt, raw.slice
              • L43 · linearToOklch calls (conditional paths may differ): Math.cbrt, Math.sqrt, Math.atan2
              • L56 · oklchToLinear calls (conditional paths may differ): Math.cos, Math.sin, Math.max, Math.min, clamp
              • L73 · linearToHex calls (conditional paths may differ): toUpperCase, padStart, toString, Math.round, Math.max, Math.min, gam, channel
              • L90 · prefersWhiteForeground calls (conditional paths may differ): luminance, oklchToLinear, contrast
              • L95 · pickForeground calls (conditional paths may differ): prefersWhiteForeground
              • L100 · pickBrandForegroundHex calls (conditional paths may differ): linearToOklch, hexToLinear, normalizeBrandHex, prefersWhiteForeground
              • L108 · isValidBrandHex calls (conditional paths may differ): test, hex.trim
              • L112 · normalizeBrandHex calls (conditional paths may differ): hex.trim, trimmed.startsWith, isValidBrandHex, normalized.toUpperCase
              • L119 · deriveBrandVars calls (conditional paths may differ): normalizeBrandHex, linearToOklch, hexToLinear, round, Math.min, Math.max, oklchStr, pickForeground
              • L171 · deriveNeutralVars calls (conditional paths may differ): Object.fromEntries, map, Object.entries, l.toFixed
              • L176 · derivePortalThemeVars calls (conditional paths may differ): deriveNeutralVars, deriveBrandVars
              • L183 · styleObjectToCss calls (conditional paths may differ): join, map, Object.entries
              • L189 · parseOrgMetadata calls (conditional paths may differ): Array.isArray, JSON.parse
              • L200 · parseBranding calls (conditional paths may differ): parseOrgMetadata, brandingSchema.safeParse
              • L206 · resolveBranding calls (conditional paths may differ): parseBranding, normalizeBrandHex
              • L216 · mergeBrandingMetadata calls (conditional paths may differ): parseOrgMetadata, brandingSchema.parse
              shared/utils/conversation-time.ts ↗
              • L1 · formatConversationTime calls (conditional paths may differ): Number.isFinite, date.getTime, Math.floor, Math.max, date.toLocaleDateString, date.getFullYear, getFullYear
              shared/utils/identity.ts ↗
              • L6 · stableIdHash calls (conditional paths may differ): seed.charCodeAt, Math.imul
              • L17 · guestTag calls (conditional paths may differ): padStart, toString, stableIdHash
              • L25 · avatarHue calls (conditional paths may differ): stableIdHash
              shared/utils/markdown.ts ↗
              • L3 · stripMarkdown calls (conditional paths may differ): trim, replace, text.replace
              • L37 · generateExcerpt calls (conditional paths may differ): stripMarkdown, plain.slice
              shared/utils/guest.ts ↗
              • L33 · parseGuestAccess calls (conditional paths may differ): guestAccessSchema.safeParse, parseOrgMetadata
              • L38 · resolveGuestAccess calls (conditional paths may differ): parseGuestAccess
              • L47 · mergeGuestAccessMetadata calls (conditional paths may differ): parseOrgMetadata, guestAccessSchema.parse
              • L73 · isGuestEmail calls (conditional paths may differ): endsWith, email.toLowerCase
              shared/utils/color.ts ↗
              • L4 · hexToRgba calls (conditional paths may differ): hex.replace, Number.parseInt, h.substring
              • L16 · lighten calls (conditional paths may differ): hex.replace, Number.parseInt, h.substring, Math.max, Math.min, hue2rgb, padStart, toString, Math.round, toHex
              shared/utils/portal-modules.ts ↗
              • L19 · parsePortalModules calls (conditional paths may differ): portalModulesSchema.safeParse, parseOrgMetadata
              • L24 · resolvePortalModules calls (conditional paths may differ): parsePortalModules
              • L29 · mergePortalModulesMetadata calls (conditional paths may differ): parseOrgMetadata, portalModulesSchema.parse
              shared/utils/widget-settings.ts ↗
              • L46 · countEnabledRules calls (conditional paths may differ): WIDGET_BUILTIN_RULE_IDS.filter, disabledBuiltins.includes, customRules.filter
              • L52 · resolveWidgetSettings calls (conditional paths may differ): WIDGET_BUILTIN_RULES.map, disabledBuiltins.includes, countEnabledRules
              • L76 · getEnabledSupportRules calls (conditional paths may differ): resolveWidgetSettings, map, settings.rules.builtins.filter, settings.rules.custom.filter
              Build and deployment pipeline · 2 GitHub Actions workflows

              Repository CI declarations, separate from runtime request processing. Job dependencies and conditions are shown as written; long commands are shortened with an ellipsis; a workflow file does not prove a recent successful run.

              CI · .github/workflows/ci.yml ↗

              Triggers: pull_request, push, workflow_dispatch

              build · no job dependencies declared

              1. Checkoutactions/checkout@v4
              2. Setup pnpmpnpm/action-setup@v4
              3. Setup Node.jsactions/setup-node@v4
              4. Install dependenciespnpm install --frozen-lockfile
              5. Build (Node server)pnpm build

              docker-build · no job dependencies declared

              1. Checkoutactions/checkout@v4
              2. Set up Docker Buildxdocker/setup-buildx-action@v3
              3. Validate Docker build (no push)docker/build-push-action@v6
              Publish Docker image · .github/workflows/docker-publish.yml ↗

              Triggers: push, workflow_dispatch

              build-and-push · no job dependencies declared

              1. Checkoutactions/checkout@v4
              2. Set up Docker Buildxdocker/setup-buildx-action@v3
              3. Log in to GHCRdocker/login-action@v3
              4. Extract metadatadocker/metadata-action@v5
              5. Build and pushdocker/build-push-action@v6
              package.json ↗
              • build: NODE_OPTIONS="--max-old-space-size=4096 $NODE_OPTIONS" nuxt build && mkdir -p .output/server/db && cp -r server/db/migrations .output/server/db/migrations
              • build:cf: NITRO_PRESET=cloudflare-module NODE_OPTIONS="$NODE_OPTIONS --max-old-space-size=8192" pnpm build
              • build:vercel: pnpm migrate && NITRO_PRESET=vercel pnpm build
              • deploy:cf: wrangler deploy --cwd .output
              • migrate: drizzle-kit migrate

              Full upstream document by @linkcraftstudio · README.md · snapshot 697d3cc

              FeedLog

              FeedLog

              Open-source feedback & roadmap tool. Self-host on Cloudflare Workers, Vercel, or Docker.

              License: MIT Discussions Stars

              Live Demo · Documentation · Discussions


              Collect product feedback, run a public roadmap, and publish changelogs — from a tool you own and control.

              FeedLog is a feedback-collection and product-communication hub for software teams. Users post ideas, vote, and discuss; teams triage into a roadmap and publish changelog entries when features ship. Built on Nuxt 4, it deploys to three platforms with zero vendor lock-in.

              FeedLog feedback board

              Why FeedLog?

              • 🌐 Deploy anywhere. Cloudflare Workers, Vercel, or Docker — pick your stack, keep your data.
              • 🤖 AI-native, not AI-washed. Similar-idea detection and AI-assisted changelog drafting — first-class features, not bolted-on chatbots.
              • 🎨 Modern Vue stack. Nuxt 4, TypeScript, Drizzle ORM, Tailwind CSS v4, shadcn-vue.
              • 🔓 MIT licensed, open core. This repository is the complete current product. Future enterprise add-ons live separately; the open-source version stays fully functional.
              • 📦 Own your data. Postgres + blob storage (Cloudflare R2, Vercel Blob, or any S3-compatible: AWS, MinIO, Alibaba OSS).

              Live Demo

              Try FeedLog running in production: feedback.feedlog.ai

              The roadmap, changelog, and feedback boards you see at that URL are all FeedLog itself running in production — every feature listed below was first requested, voted on, and shipped through it.

              Self Hosting

              FeedLog is built to be self-hosted. Three deploy targets cover the common cases: serverless platforms (Cloudflare Workers, Vercel) for zero-ops managed hosting, and Docker for full control on a VPS / NAS / Docker PaaS.

              Whichever path you pick, you'll need a PostgreSQL 17+ with the vector extension reachable from the runtime. Neon and Supabase both enable it via their UI; on self-hosted Postgres run CREATE EXTENSION IF NOT EXISTS vector; once.

              A Deploy with Cloudflare Workers or Vercel

              Both platforms offer a one-click deploy that forks the repo into your account and prompts for the required environment variables in the same form. Build and deploy run automatically; no local toolchain needed.

              Deploy to Cloudflare Workers Deploy with Vercel
              • Cloudflare Workers uses Hyperdrive (Postgres acceleration) and R2 (blob storage). On first request, the deployed Worker serves a /setup page that runs migrations against your Hyperdrive-bound database. Full guide: docs/deploy/cloudflare-workers.md.
              • Vercel runs pnpm migrate && pnpm build in the build step; failed migrations abort the deploy and leave the previous version serving traffic. Pair with Neon or Supabase from the Vercel Marketplace for the fastest setup. Full guide: docs/deploy/vercel.md.

              Keep your fork up to date

              Both platforms deploy from a fork of this repo, so to pick up upstream fixes you'll want either a manual git pull from linkcraftstudio/feedlog, or a scheduled GitHub Action (e.g. tgymnich/fork-sync) that opens a PR against your fork's main whenever upstream changes.

              B Deploy with Docker

              Multi-arch image (linux/amd64 + linux/arm64) on ghcr.io/linkcraftstudio/feedlog. Works on any Linux host — VPS, NAS, Coolify, Dokploy, Portainer, Kubernetes.

              Quick start with the bundled compose.yml

              The repo ships a compose.yml that brings up the app and a pgvector-enabled Postgres in one command. Three lines:

              git clone https://github.com/linkcraftstudio/feedlog.git && cd feedlog
              echo "BETTER_AUTH_SECRET=$(openssl rand -hex 32)" > .env
              echo "SYSTEM_ADMIN_EMAILS=you@example.com" >> .env
              docker compose up -d
              

              The app is at http://localhost:3000. Sign up with an email listed in SYSTEM_ADMIN_EMAILS to land as admin. Optional variables (OAuth, AI, transactional email) can be added to the same .env — compose.yml passes them through to the app container.

              Single-container run

              If you bring your own Postgres (with pgvector):

              docker run -d --name feedlog -p 3000:3000 \
                -e DATABASE_URL="postgresql://user:pass@host:5432/feedlog" \
                -e BETTER_AUTH_SECRET="$(openssl rand -hex 32)" \
                -e SYSTEM_ADMIN_EMAILS="you@example.com" \
                ghcr.io/linkcraftstudio/feedlog:v0.1.0
              

              The default image falls back to NuxtHub's local-filesystem driver for uploads — fine for a trial; mount a volume on /app/.data to make those uploads survive container restarts. For S3-compatible storage (AWS S3 / R2 / MinIO / Alibaba OSS / etc.), set the S3_* env vars at runtime — no rebuild needed. See docs/deploy/docker.md.

              [!TIP] Both quick starts pin to :v0.1.0. In production prefer an immutable tag (:vX.Y.Z or :sha-abc1234) over :latest to avoid surprise upgrades on docker pull.

              Environment variables

              The three variables in the Required table are the minimum to boot a working install. The most useful optional variables are listed below; the full configuration reference (and .env.example as the annotated source) covers everything else — S3 storage, transactional email, platform-specific overrides, and authentication toggles.

              Required

              Variable Required Description Example
              DATABASE_URL Yes PostgreSQL 17+ connection string. The database must have the vector extension installed. On Cloudflare Workers, this is replaced by the Hyperdrive binding. postgresql://user:pass@host:5432/feedlog?sslmode=require
              BETTER_AUTH_SECRET Yes Session and cookie encryption secret, 32+ random characters. Generate with openssl rand -hex 32. Rotating it invalidates every existing session. 0a1b2c3d4e5f... (64 hex chars)
              SYSTEM_ADMIN_EMAILS Yes Comma-separated emails granted admin role on first sign-up. Set this before any user signs up — otherwise the install has no admin and the dashboard is locked. you@example.com,ops@example.com

              Common optional

              Variable Required Description Example
              BETTER_AUTH_URL No Public URL of the app (no trailing slash). Inferred from the request Host header by default. Set explicitly behind Host-rewriting proxies or to anchor OAuth callback URLs. https://feedback.yourdomain.com
              GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET No Enable Google OAuth. Authorized redirect URI: <BETTER_AUTH_URL>/api/auth/callback/google. …apps.googleusercontent.com
              GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET No Enable GitHub OAuth. Callback URL: <BETTER_AUTH_URL>/api/auth/callback/github. Iv1.abc123…
              OPENAI_API_KEY No Enable AI features (similar-idea detection, AI-drafted changelog entries). Without it, those features are silently disabled. sk-…
              OPENAI_BASE_URL No Override the OpenAI endpoint — works with Azure OpenAI, LiteLLM, self-hosted gateways. Defaults to https://api.openai.com/v1. https://your-litellm-host/v1
              RESEND_API_KEY No Enable transactional email via Resend (password resets, optional sign-up verification). Without it, emails are logged to stdout. re_…
              S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY / S3_BUCKET (+ S3_ENDPOINT / S3_REGION) No Enable S3-compatible blob storage at runtime — works with AWS S3, Cloudflare R2, MinIO, Alibaba OSS, Tencent COS, etc. Without these, uploads use NuxtHub's local-filesystem driver. See the service cheat sheet. LTAI5t… / … / feedlog-prod (+ endpoint/region)

              Features

              • Feedback boards — Organize ideas into public or private boards with voting, categories, and status workflows.
              • Roadmap — Drag-and-drop view of planned, in-progress, and shipped work.
              • Changelog — Publish release notes with AI-drafted style presets (concise, structured, benefit-led, witty).
              • Similar-idea merge — Vector embeddings (OpenAI text-embedding-3-large, 768-dim) surface duplicate feedback before it fragments the board.
              • Authentication — Email / password, Google OAuth, GitHub OAuth, admin role handling — powered by better-auth.
              • Comments & discussions — Markdown-powered threads on every post.
              • Notifications — Authors and upvoters are subscribed automatically and emailed when a post's status changes or the team replies officially; unsubscribe per post at any time. Admins aren't notified. Requires an email provider.
              • Inbox — Manage Widget conversations, human handoffs and reply email notifications.

              Tech Stack

              Layer Choice
              Framework Nuxt 4 (Vue 3, Nitro, TypeScript)
              Database PostgreSQL 17+ with pgvector for embeddings
              ORM / driver Drizzle ORM + postgres-js
              Authentication better-auth (email/password + Google OAuth + GitHub OAuth + admin plugin)
              AI OpenAI-compatible API (OpenAI, Azure, LiteLLM, Ollama…)
              Blob storage Cloudflare R2 binding, Vercel Blob, or S3-compatible via aws4fetch (AWS, MinIO, OSS)
              State Pinia
              UI Tailwind CSS v4, shadcn-vue (new-york), Lucide icons
              Editor md-editor-v3
              Deploy targets Cloudflare Workers, Vercel, Node.js Docker
              Package manager pnpm

              Local Development

              git clone https://github.com/linkcraftstudio/feedlog.git
              cd feedlog
              pnpm install
              cp .env.example .env   # fill in DATABASE_URL, BETTER_AUTH_SECRET, SYSTEM_ADMIN_EMAILS
              pnpm migrate           # initialize database schema
              pnpm dev               # http://localhost:3000
              

              See CONTRIBUTING.md for the full development setup and PR process.

              Roadmap

              Live, user-voted roadmap: feedback.feedlog.ai/roadmap.

              Release history: see CHANGELOG.md.

              Contributing

              Pull requests, bug reports, and feature discussions are welcome.

              Security

              Found a vulnerability? Please email feedlog.oss@outlook.com rather than filing a public issue. See SECURITY.md for details and response timelines.

              Community

              License

              MIT © 2026 LinkCraft Studio

              Frequently asked about FeedLog

              What is FeedLog?+

              FeedLog is a self-hosted Canny/Featurebase alternative built on the Cloudflare developer platform. Own your feedback board, votes and public roadmap on Cloudflare Workers

              What does FeedLog replace?+

              FeedLog is listed as an alternative to Canny, Featurebase. Compare the features and tradeoffs before migrating.

              What Cloudflare primitives does FeedLog use?+

              FeedLog is built on Hyperdrive, Queues, R2, Workers.

              How much does FeedLog cost to run?+

              Plan for Workers Paid at US$5 per account/month plus usage, and external PostgreSQL 17+ with vector through Hyperdrive. Neon Free is a limited database option (100 CU-hours/month, 0.5 GB storage, 5 GB transfer per project). R2 uploads, optional AI/email and maintenance need separate budgets; no zero-cost or measured-bill claim. The Workers Paid minimum is account-wide, not a separate $5 subscription for every app; usage beyond included allowances can add charges. Neon Free provides 100 CU-hours monthly, 0.5 GB storage and 5 GB network transfer per project; select PostgreSQL 17+ and enable vector before the first migration. A continuously busy database or retained feedback, attachments and embeddings can exhaust database allowances; check actual provider usage. Hyperdrive connection pooling and caching are included in Workers Paid; the external database is a separate service. Configure the feedlog-inbox Queue producer and consumer; writes, reads, deletes and retries consume operations. R2 BLOB uploads are optional at the application level. Standard R2 included storage is 10 GB-month per month, with request allowances and metered usage beyond limits. OpenAI-powered features require provider credentials and models. Resend email requires separate provider setup; neither is included in the Workers subscription. Without OAuth, email/password sign-in defaults on. Without Resend, email verification defaults off and password-reset email is unavailable; choose authentication settings deliberately. A real Hyperdrive ID, public auth URL, auth secret and admin-bootstrap emails are required; shipped values are placeholders. This is a source-based planning budget, not a fresh deployment, performance benchmark, security audit or guaranteed bill. Check current Cloudflare pricing before deploying.

              Is FeedLog open source?+

              The upstream repository declares the MIT license. Read its terms at https://raw.githubusercontent.com/linkcraftstudio/feedlog/697d3cc7becccc4d38f57333b2acd13dbfb3d6c6/LICENSE. Source code and contributor credit are available at https://github.com/linkcraftstudio/feedlog.

              Community rating

              No ratings yet. Tried this project? Share your experience.

              One rating per verified account. You can change or remove yours. Accounts are email verified; use of the software is self-reported.

              Discuss your experience ↓
              Sign in to rate

              Discussion · 0

              sign in to comment →
              No comments yet — be the first.