Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,16 @@ ENV NODE_OPTIONS="--max-old-space-size=$NODE_HEAP_MB --require /app/platform/scr
ARG SITE_URL=http://cbk.localhost:3000
ENV SITE_URL=$SITE_URL

# @note space and portal sites use their apexes at runtime through proxy.ts;
# the remaining apex rewrites are generated at build time, so their runtime
# values must match the build
# @todo move the remaining apex host rewrites into the runtime proxy
# so one image digest serves any domain without a rebuild
# @note space, portal and app hosts are selected at runtime through proxy.ts;
# partner host rewrites are generated at build time, so their runtime values
# must match the build
ARG APP_APEX=
ENV APP_APEX=$APP_APEX
ARG PARTNERS_APEX=
ENV PARTNERS_APEX=$PARTNERS_APEX

# @note the app-shell hosts are rewrites of the same kind: the main shell at
# `cbk-apps.localhost`, the labs shell at `cbk-labs.localhost`. The runtime
# origins must match these too
# @note build defaults only; shell host routing uses the values supplied at
# server startup
ARG APP_MAIN_ORIGIN=http://cbk-apps.localhost:3000
ENV APP_MAIN_ORIGIN=$APP_MAIN_ORIGIN
ARG APP_LABS_ORIGIN=http://cbk-labs.localhost:3000
Expand Down
8 changes: 3 additions & 5 deletions docker/distro/community/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,12 @@ services:
# needs an address it can reach instead (and TLS if the site has it)
RELAY_PORT: 3001
RELAY_URL: ${RELAY_URL:-http://cbk-relay.localhost:3001}
# @note space and portal subdomains read their apexes at server startup;
# @note space, portal and app hosts read their settings at server startup;
# recreate the container to change domains without rebuilding the image
SPACE_APEX: ${SPACE_APEX:-cbk-space.localhost}
PORTAL_APEX: ${PORTAL_APEX:-cbk-portal.localhost}
# @note the app shells, baked the same way: the main shell at
# `cbk-apps.localhost:3000`, the labs shell at
# `cbk-labs.localhost:3000`. Cookies do not cross hosts, so sign in on
# the shell host itself
APP_APEX: ${APP_APEX:-}
# @note cookies do not cross hosts, so sign in on the shell host itself
APP_MAIN_ORIGIN: ${APP_MAIN_ORIGIN:-http://cbk-apps.localhost:3000}
APP_LABS_ORIGIN: ${APP_LABS_ORIGIN:-http://cbk-labs.localhost:3000}
# @note left empty, the image generates these secrets on first boot and
Expand Down
8 changes: 3 additions & 5 deletions docker/distro/studio/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,12 @@ services:
# needs an address it can reach instead (and TLS if the site has it)
RELAY_PORT: 3001
RELAY_URL: ${RELAY_URL:-http://cbk-relay.localhost:3001}
# @note space and portal subdomains read their apexes at server startup;
# @note space, portal and app hosts read their settings at server startup;
# recreate the container to change domains without rebuilding the image
SPACE_APEX: ${SPACE_APEX:-cbk-space.localhost}
PORTAL_APEX: ${PORTAL_APEX:-cbk-portal.localhost}
# @note the app shells, baked the same way: the main shell at
# `cbk-apps.localhost:3000`, the labs shell at
# `cbk-labs.localhost:3000`. Cookies do not cross hosts, so sign in on
# the shell host itself
APP_APEX: ${APP_APEX:-}
# @note cookies do not cross hosts, so sign in on the shell host itself
APP_MAIN_ORIGIN: ${APP_MAIN_ORIGIN:-http://cbk-apps.localhost:3000}
APP_LABS_ORIGIN: ${APP_LABS_ORIGIN:-http://cbk-labs.localhost:3000}
# @note left empty, the image generates these secrets on first boot and
Expand Down
44 changes: 38 additions & 6 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,14 @@ and `PORTAL_APEX=cbk-portal.localhost`. Setting `PORTAL_APEX=portal.localhost`
serves a portal named `test` at `http://test.portal.localhost:3000/`, with its
existing authentication and app configuration.

The other apex host rewrites are still generated when Next builds, so their
runtime values must match the image; see
Standalone app host routing also reads `APP_APEX` at server startup. Setting
`APP_APEX=app.localhost` serves a registered app such as `chat` at
`http://chat.app.localhost:3000/`. Only apps included in the image's manifest
catalogue receive subdomain routes. Community and Studio leave this apex empty
by default, so apps remain available by path.

Partner host rewrites are still generated when Next builds, so `PARTNERS_APEX`
must match the image's build-time value; see
[Deployment](./deployment.md#production-boundary).

## App shell origins
Expand All @@ -239,10 +245,12 @@ APP_MAIN_ORIGIN=https://apps.example.com
APP_LABS_ORIGIN=https://labs.example.com
```

Like the apexes, the shell host rewrites are generated when Next builds, so
the runtime origins must match the build. The community image bakes
`APP_MAIN_ORIGIN=http://cbk-apps.localhost:3000` and
`APP_LABS_ORIGIN=http://cbk-labs.localhost:3000`.
Shell host routing reads these origins at server startup. Recreate the container
after changing them; no image rebuild is needed. The Community and Studio stacks
default to `APP_MAIN_ORIGIN=http://cbk-apps.localhost:3000` and
`APP_LABS_ORIGIN=http://cbk-labs.localhost:3000`. Existing app paths, sign-in,
callbacks and manifests remain available on the new hosts. Cookies do not cross
hosts, so sign in on the shell host itself.

## `HOSTS_CONFIG`

Expand Down Expand Up @@ -414,6 +422,30 @@ only when the reverse proxy removes client-supplied forwarded headers, writes
its own values, and prevents clients from reaching the application origin
directly. If those conditions cannot be guaranteed, leave it unset.

### Server Actions origin checks

Next.js checks Server Actions before the platform sets up request context.
It compares the browser's `Origin` host, including any port, with
`x-forwarded-host` when present, otherwise `Host`. This framework check runs
independently of `TRUST_PROXY_HEADERS`. A reverse proxy must overwrite forwarded
headers with the public request host, including its port. Preserve the public
`Host` as well when using the platform's space, portal and app host routing.

Ordinary page actions post back to the page's public host. Deployed builds
configure no allowed-origin exceptions, including for deployment-owned domains.
An origin that differs from the public forwarded host is rejected.
Host-classification headers do not grant cross-origin access.

A portal gateway may route to an internal backend hostname. It must preserve
the browser's original `Origin` and overwrite `x-forwarded-host` with the public
portal host. Replacing `Origin` with the backend origin breaks the comparison
and hides where the browser request actually originated.

Only the development server retains exceptions for `localhost:8080` through
`localhost:8089` and `localhost:9090` through `localhost:9099`. These support
the local proxy's deliberate host impersonation and are absent from production
builds. Deployment domains do not add exceptions in either mode.

## Platform capacity cap

`PLATFORM_MAX_TOKENS_PER_MONTH` is an optional deployment-wide safety ceiling,
Expand Down
12 changes: 6 additions & 6 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ and keep secrets out of image layers.

The current community image deliberately bakes the neutral single-host
topology: `SITE_URL=http://cbk.localhost:3000`, with no external zones.
The two app shells answer at `http://cbk-apps.localhost:3000` and
The Community and Studio stacks configure the two app shells at
`http://cbk-apps.localhost:3000` and
`http://cbk-labs.localhost:3000` through `APP_MAIN_ORIGIN` and
`APP_LABS_ORIGIN`.
Browsers resolve any `*.localhost` name to loopback, so a space site published
Expand All @@ -289,11 +290,10 @@ stacks default `SPACE_APEX` to `cbk-space.localhost` and `PORTAL_APEX` to
`cbk-portal.localhost`. Space and portal routing read these values at server
startup. Changing them and recreating the container moves those sites to the
new domains without rebuilding the image. Portal authentication and app
configuration continue to apply on the new domain.

The remaining apexes and app-shell origins must name the same hosts as the
build, which the Compose files ensure; changing those hosts still needs a
rebuild with the matching build arguments. Runtime service variables
configuration continue to apply on the new domain. App hosts work the same way
through `APP_APEX`, `APP_MAIN_ORIGIN` and `APP_LABS_ORIGIN`; see
[Configuration](./configuration.md#app-shell-origins). Only `PARTNERS_APEX`
still has to match the build. Runtime service variables
such as the database, Redis, Qdrant and S3-compatible storage endpoints remain
configurable. Deployment identity that Next currently exposes through
`next.config.js` is still frozen at build time; do not present the same digest
Expand Down
6 changes: 4 additions & 2 deletions platform/app/apps/chat/components/ChatArea.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,11 @@ export function ChatArea({
{...props}
className={clsx(
'chat-area',
'sticky left-0 right-0 bottom-0',
'left-0 right-0 bottom-0',
{
'pb-2': hasMessages,
// @note sticky positioning can pull the empty composer over the centered intro
'sticky pb-2': hasMessages,
relative: !hasMessages,

// @note used to hide the the text as it scrolls behind the area

Expand Down
10 changes: 5 additions & 5 deletions platform/components/Auth.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,11 @@ export default function Auth({
const title = _title || 'ChatBotKit'

return sessionStatus === 'loading' ? null : (
<form {...props} className={clsx('space-y-4', className)} ref={formRef}>
<form
{...props}
className={clsx('w-full max-w-sm space-y-4', className)}
ref={formRef}
>
{partner ? (
<>
<PartnerBanner className="text-7xl" partner={partner} />
Expand Down Expand Up @@ -418,10 +422,6 @@ export default function Auth({
<ChevronRightIcon className="w-[1em] h-[1em]" />
</button>
</div>
<p className="text-xs">
This deployment trusts whoever reaches it: no code is sent
and the account is created on first use.
</p>
</div>
) : null}
{providers
Expand Down
3 changes: 3 additions & 0 deletions platform/layouts/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Link from '@/components/Link'
import MenuButton from '@/components/MenuButton'
import Meta from '@/components/Meta'
import { NestedAccordionMenu } from '@/components/NestedAccordion'
import NoRubberBand from '@/components/NoRubberBand'
import NotificationsButton from '@/components/NotificationsButton'
import PartnerBanner from '@/components/PartnerBanner'
import ProfileBar from '@/components/ProfileBar'
Expand Down Expand Up @@ -656,6 +657,8 @@ export default SessionContext.withSessionContext(function Dashboard({
enabled={isBuilderExperience}
ownerId={session?.user?.id}
>
{/* @note suppress document bounce while preserving overscroll inside panels */}
<NoRubberBand />
<Confirm>
<div className="min-h-[calc(100vh-4rem)] auto-bg-white">
<Meta
Expand Down
Loading