Configuration
Configure the public portal, connector, authentication, and protection policies.
Branding
feedbax.config.mjs is the public portal's single presentation source. Change the configuration and referenced assets; product components do not need editing.
import { defineConfig } from '@feedbax/config'
import { notionConnector } from '@feedbax/notion'
export default defineConfig({
publicUrl: 'https://feedback.example.com',
branding: {
productName: 'Acme Feedback',
description: 'Help shape what Acme builds next.',
logo: '/brand/acme.svg',
favicon: '/brand/favicon.svg',
accentColor: '#3157d5',
themes: {
light: {
background: '#f7f8fb',
surface: '#ffffff',
text: '#202331',
mutedText: '#5f6678',
border: '#d3d7e2',
},
dark: {
background: '#0d1018',
surface: '#151925',
text: '#eef1f7',
mutedText: '#abb3c3',
border: '#3b4354',
},
},
navigation: [
{ label: 'Feedback', href: '/' },
{ label: 'Roadmap', href: '/roadmap' },
{ label: 'Updates', href: '/changelog' },
],
poweredByFeedbax: true,
},
connector: notionConnector,
})Colors must use six-digit hex notation. Core text pairs must meet a 4.5:1 contrast ratio and the accent must meet 3:1 against both surfaces. Invalid colors fail during configuration parsing with the affected field named.
Logo and favicon values accept root-relative public files or HTTPS URLs. Supported logo formats are SVG, PNG, WebP, and JPEG; favicons accept SVG, PNG, or ICO. Root-relative files are checked during every portal build. HTTPS URLs receive syntax and file-type validation, but remote availability is the deployer's responsibility.
navigation is the complete ordered portal navigation. Destinations must be unique and root-relative or HTTPS. poweredByFeedbax: false removes the attribution entirely.
Application configuration
A connector descriptor has an id, displayName, and declared capability list. The Notion descriptor currently declares the comments capability.
Authentication is configured through the typed authentication property. It declares the audience, exact issuer/key registry, session encryption key ring and active key ID, and host login URL. Secret values should be populated from environment variables rather than committed to configuration.
mutationProtection configures exact allowed origins, the maximum JSON body size, a network-level request limit, and separate submit, vote, comment, and subscription policies. Limits and windows must be positive integers. CAPTCHA flags only activate when a server-side CaptchaProvider is installed; no vendor is enabled by default.
The bundled memory rate limiter is intended for development or a single Node/Docker process. Cloudflare, Vercel, and multi-process deployments must inject a shared durable RateLimitStore, otherwise limits are not coordinated across instances.
Notion vote ledger
Unique voting requires NOTION_VOTES_DATA_SOURCE_ID and a server-only, high-entropy FEEDBAX_INTERACTION_HASH_KEY. The vote data source uses a title Key, relation Feedback, rich-text Voter key, and checkbox Active; the feedback data source must expose a writable number property named Vote count.
The browser sends only the desired state and feedback ID. Feedbax derives identity from the signed session and stores an HMAC-derived opaque key. Connector-only voting is serialized within one application process, but Notion cannot enforce uniqueness or update the ledger and total atomically across multiple instances. Route vote mutations through one instance when using this mode.
Notion comments
Authenticated comments require NOTION_COMMENTS_DATA_SOURCE_ID. The comments data source uses a title Key, relation Feedback, rich-text fields Body, Author ID, and Author name, optional URL Author avatar, and select Author kind. The feedback data source keeps the denormalized Comment count, so board reads do not issue per-item comment requests.
commentRoles.administrator and commentRoles.team map verified session role claims to public response badges; administrator roles take precedence. Unknown roles are shown as customers. Feedbax never persists the authenticated email in comments, and comment bodies containing email-like addresses are rejected.
Retries reuse the same client request ID. Feedbax serializes comment mutations per feedback item within one process, but Notion has no unique constraint or multi-record transaction. Route comment mutations through one instance for connector-only deployments.
Public roadmap
The roadmap is a Kanban view of the existing feedback data source. Configure its copy and ordered canonical columns separately from Notion's workflow labels:
roadmap: {
title: 'Product roadmap',
description: 'Follow what we are considering, building, and shipping.',
columnStatusIds: ['open', 'planned', 'in-progress', 'complete'],
}Every column ID must reference a unique entry in publicTaxonomy.statuses. Configured columns remain visible when empty and stack vertically on small screens.
The Notion connector maps one or more workflow values to each canonical public status. The first value is used when Feedbax needs to write that status:
statuses: {
open: ['Open', 'Under review'],
planned: ['Planned', 'Ready for engineering'],
'in-progress': ['In progress', 'Quality assurance'],
complete: ['Complete'],
}Only mapped values are public. Feedback using an unmapped Notion status is excluded from lists, roadmap responses, duplicate suggestions, and direct detail pages. Raw Notion status names are never returned to the browser; several internal values may safely collapse into one public label.
Roadmap reads share the feedback and roadmap cache tags. After an external Notion status edit, invalidate those tags through the deployment's mutation or webhook integration; the next roadmap fetch will read the new status and regroup the card.
Planned configuration
Portable cache invalidation providers and additional feature switches remain planned. Their public shape is deliberately undocumented until implemented.