From 3a3123aa9cd31533168e2764e2a1dbdbf06ddf4f Mon Sep 17 00:00:00 2001 From: pdparchitect Date: Thu, 10 Sep 2026 11:06:02 +0000 Subject: [PATCH] chore(release): bump version to 0.3.0 and update changelog (+1 more) - chore(release): bump version to 0.3.0 and update changelog - feat: update endpoint manifests and port configurations for Community and Studio stacks --- CHANGELOG.md | 21 ++++++ docker/distro/community/compose.yml | 86 ++++++++++++++++++++--- docker/distro/studio/compose.yml | 101 ++++++++++++++++++++++----- docs/configuration.md | 3 +- docs/deployment.md | 102 +++++++++++++++++++++------- package.json | 2 +- 6 files changed, 260 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e001846..44c49b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,27 @@ here. The release version is defined in the workspace root `package.json`. ## [Unreleased] +## [0.3.0] - 2026-09-10 + +### Added + +- Declare where the Community and Studio Compose stacks answer in an `x-cbk` + endpoint manifest inside each published artifact, resolved with + `docker compose config --format json`, so a launcher reads the stack's + addresses instead of assuming port 3000. + +### Changed + +- Move the published application port with `PLATFORM_PORT` and the site + hostname with `PLATFORM_HOST` in the Community and Studio stacks. The site, + sign-in, app shell, space and portal addresses follow the port, and + `RELAY_URL` now follows `RELAY_PORT`, so a second instance on one host needs + the port variables rather than an override file. +- Publish the Studio stack on `31000`, `31001` and `31900` instead of + Community's `3000`, `3001` and `3900`, so it runs beside a developer's + existing services on `3000` and beside a Community stack on the same host. + Container ports are unchanged. + ## [0.2.2] - 2026-09-10 ### Fixed diff --git a/docker/distro/community/compose.yml b/docker/distro/community/compose.yml index a3f792c..64a16db 100644 --- a/docker/distro/community/compose.yml +++ b/docker/distro/community/compose.yml @@ -19,11 +19,73 @@ # The developer stack with profiles, source builds and hot reload lives in # docker-compose.yml at the repository root; this file never builds anything. # +# Where the stack answers is not fixed: PLATFORM_PORT moves the application +# (and with it the app shells, spaces and portals), RELAY_PORT the realtime +# relay and STORAGE_PORT the object store, and every address a browser or a +# host process dials is declared once in the `x-cbk` manifest below. A +# launcher such as ChatBotKit Studio reads the resolved manifest back with +# `docker compose config --format json` rather than assuming port 3000. +# # One folder per package flavor lives under docker/distro/. Each publishes as # ghcr.io/chatbotkit/platform-, pinned by CI to the matching # application and initializer image digests of the same flavor. # ============================================================================= +# @note the endpoint manifest. Each entry is one address that something +# outside the Compose network dials - a browser, a desktop launcher, the +# application itself when it fetches a presigned URL - with the service that +# answers, the host port it is published on and the variable that overrides +# it. The `url` values are the same expressions the services receive (the +# anchors below feed `environment`), so `docker compose config --format json` +# resolves them against the caller's environment and `.["x-cbk"]` is the exact +# addressing this stack runs with; no port is assumed on either side. Plain +# Compose ignores `x-` keys. Bump `version` when an entry changes shape. +# +# Hostnames stay under `*.localhost`, which browsers resolve to loopback with +# no DNS setup. A launcher that forwards the published ports must route every +# host named here, including the wildcard apexes, to the same ports. +x-cbk: + version: 1 + endpoints: + site: + service: platform + published: ${PLATFORM_PORT:-3000} + variable: SITE_URL + url: &site-url ${SITE_URL:-http://${PLATFORM_HOST:-cbk.localhost}:${PLATFORM_PORT:-3000}} + apps: + service: platform + published: ${PLATFORM_PORT:-3000} + variable: APP_MAIN_ORIGIN + url: &apps-url ${APP_MAIN_ORIGIN:-http://cbk-apps.localhost:${PLATFORM_PORT:-3000}} + labs: + service: platform + published: ${PLATFORM_PORT:-3000} + variable: APP_LABS_ORIGIN + url: &labs-url ${APP_LABS_ORIGIN:-http://cbk-labs.localhost:${PLATFORM_PORT:-3000}} + relay: + service: platform + published: ${RELAY_PORT:-3001} + variable: RELAY_URL + url: &relay-url ${RELAY_URL:-http://cbk-relay.localhost:${RELAY_PORT:-3001}} + storage: + service: garage + published: ${STORAGE_PORT:-3900} + variable: STORAGE_URL + url: &storage-url ${STORAGE_URL:-http://cbk-storage.localhost:${STORAGE_PORT:-3900}} + # @note wildcard host families served on the site endpoint's port: a space + # published as `acme` answers at `http://acme.:` + apexes: + space: + service: platform + published: ${PLATFORM_PORT:-3000} + variable: SPACE_APEX + apex: &space-apex ${SPACE_APEX:-cbk-space.localhost} + portal: + service: platform + published: ${PLATFORM_PORT:-3000} + variable: PORTAL_APEX + apex: &portal-apex ${PORTAL_APEX:-cbk-portal.localhost} + # @note the storage environment every application service shares. The store # is Garage, spoken to over the plain S3 protocol. Left empty, the access key # is generated by Garage on first boot and persisted in the platform-data @@ -38,7 +100,7 @@ # so the two must agree. Set STORAGE_URL to an address both can reach (and # TLS if the site has it) when browsers do not reach the machine itself. x-storage-env: &storage-env - STORAGE_ENDPOINT: ${STORAGE_URL:-http://cbk-storage.localhost:${STORAGE_PORT:-3900}} + STORAGE_ENDPOINT: *storage-url STORAGE_REGION: garage STORAGE_ACCESS_KEY_ID: ${STORAGE_ACCESS_KEY_ID:-} STORAGE_SECRET_ACCESS_KEY: ${STORAGE_SECRET_ACCESS_KEY:-} @@ -58,33 +120,35 @@ services: platform: image: ${PLATFORM_IMAGE:-ghcr.io/chatbotkit/platform-community-app:next} ports: - - '3000:3000' + # @note PLATFORM_PORT moves the published side, the container keeps 3000 + - '${PLATFORM_PORT:-3000}:3000' # @note the built-in realtime relay - see RELAY_URL below - '${RELAY_PORT:-3001}:3001' environment: <<: *storage-env NODE_ENV: production PORT: 3000 - SITE_URL: ${SITE_URL:-http://cbk.localhost:3000} + SITE_URL: *site-url STATIC_URL: ${STATIC_URL:-} API_URL: ${API_URL:-} HOSTS_CONFIG: ${HOSTS_CONFIG:-} - NEXTAUTH_URL: ${NEXTAUTH_URL:-http://cbk.localhost:3000} + # @note sign-in callbacks land on the site itself unless moved + NEXTAUTH_URL: ${NEXTAUTH_URL:-${SITE_URL:-http://${PLATFORM_HOST:-cbk.localhost}:${PLATFORM_PORT:-3000}}} # @note realtime channels (voice, avatars) meet at a relay the platform # process hosts itself on RELAY_PORT. Both that process and a host # browser dial RELAY_URL, so loopback serves both; a browser elsewhere # 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 host routing reads its 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} + RELAY_URL: *relay-url + # @note host routing reads its settings at server startup; recreate the + # container to change domains without rebuilding the image. See x-cbk + SPACE_APEX: *space-apex + PORTAL_APEX: *portal-apex APP_APEX: ${APP_APEX:-} PARTNERS_APEX: ${PARTNERS_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} + APP_MAIN_ORIGIN: *apps-url + APP_LABS_ORIGIN: *labs-url # @note left empty, the image generates these secrets on first boot and # persists them in the platform-data volume; set explicitly to override NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-} diff --git a/docker/distro/studio/compose.yml b/docker/distro/studio/compose.yml index ff43f81..bae57ad 100644 --- a/docker/distro/studio/compose.yml +++ b/docker/distro/studio/compose.yml @@ -19,11 +19,77 @@ # The developer stack with profiles, source builds and hot reload lives in # docker-compose.yml at the repository root; this file never builds anything. # +# Where the stack answers is not fixed: PLATFORM_PORT moves the application +# (and with it the app shells, spaces and portals), RELAY_PORT the realtime +# relay and STORAGE_PORT the object store, and every address a browser or a +# host process dials is declared once in the `x-cbk` manifest below. A +# launcher such as ChatBotKit Studio reads the resolved manifest back with +# `docker compose config --format json` rather than assuming a port. +# +# Studio publishes on 31000, 31001 and 31900 rather than Community's 3000 +# family: it runs beside whatever a developer already has on 3000, and the +# published ports are the only difference - every container keeps its port. +# # One folder per package flavor lives under docker/distro/. Each publishes as # ghcr.io/chatbotkit/platform-, pinned by CI to the matching # application and initializer image digests of the same flavor. # ============================================================================= +# @note the endpoint manifest. Each entry is one address that something +# outside the Compose network dials - a browser, a desktop launcher, the +# application itself when it fetches a presigned URL - with the service that +# answers, the host port it is published on and the variable that overrides +# it. The `url` values are the same expressions the services receive (the +# anchors below feed `environment`), so `docker compose config --format json` +# resolves them against the caller's environment and `.["x-cbk"]` is the exact +# addressing this stack runs with; no port is assumed on either side. Plain +# Compose ignores `x-` keys. Bump `version` when an entry changes shape. +# +# Hostnames stay under `*.localhost`, which browsers resolve to loopback with +# no DNS setup. A launcher that forwards the published ports must route every +# host named here, including the wildcard apexes, to the same ports. +x-cbk: + version: 1 + endpoints: + site: + service: platform + published: ${PLATFORM_PORT:-31000} + variable: SITE_URL + url: &site-url ${SITE_URL:-http://${PLATFORM_HOST:-cbk.localhost}:${PLATFORM_PORT:-31000}} + apps: + service: platform + published: ${PLATFORM_PORT:-31000} + variable: APP_MAIN_ORIGIN + url: &apps-url ${APP_MAIN_ORIGIN:-http://cbk-apps.localhost:${PLATFORM_PORT:-31000}} + labs: + service: platform + published: ${PLATFORM_PORT:-31000} + variable: APP_LABS_ORIGIN + url: &labs-url ${APP_LABS_ORIGIN:-http://cbk-labs.localhost:${PLATFORM_PORT:-31000}} + relay: + service: platform + published: ${RELAY_PORT:-31001} + variable: RELAY_URL + url: &relay-url ${RELAY_URL:-http://cbk-relay.localhost:${RELAY_PORT:-31001}} + storage: + service: garage + published: ${STORAGE_PORT:-31900} + variable: STORAGE_URL + url: &storage-url ${STORAGE_URL:-http://cbk-storage.localhost:${STORAGE_PORT:-31900}} + # @note wildcard host families served on the site endpoint's port: a space + # published as `acme` answers at `http://acme.:` + apexes: + space: + service: platform + published: ${PLATFORM_PORT:-31000} + variable: SPACE_APEX + apex: &space-apex ${SPACE_APEX:-cbk-space.localhost} + portal: + service: platform + published: ${PLATFORM_PORT:-31000} + variable: PORTAL_APEX + apex: &portal-apex ${PORTAL_APEX:-cbk-portal.localhost} + # @note the storage environment every application service shares. The store # is Garage, spoken to over the plain S3 protocol. Left empty, the access key # is generated by Garage on first boot and persisted in the platform-data @@ -38,7 +104,7 @@ # so the two must agree. Set STORAGE_URL to an address both can reach (and # TLS if the site has it) when browsers do not reach the machine itself. x-storage-env: &storage-env - STORAGE_ENDPOINT: ${STORAGE_URL:-http://cbk-storage.localhost:${STORAGE_PORT:-3900}} + STORAGE_ENDPOINT: *storage-url STORAGE_REGION: garage STORAGE_ACCESS_KEY_ID: ${STORAGE_ACCESS_KEY_ID:-} STORAGE_SECRET_ACCESS_KEY: ${STORAGE_SECRET_ACCESS_KEY:-} @@ -58,19 +124,22 @@ services: platform: image: ${PLATFORM_IMAGE:-ghcr.io/chatbotkit/platform-studio-app:next} ports: - # @note studio trusts the local user, so its ports stay on loopback - - '127.0.0.1:3000:3000' + # @note studio trusts the local user, so its ports stay on loopback; + # PLATFORM_PORT moves the published side, the container keeps 3000. + # Same for the relay (RELAY_PORT, container 3001) below + - '127.0.0.1:${PLATFORM_PORT:-31000}:3000' # @note the built-in realtime relay - see RELAY_URL below - - '127.0.0.1:${RELAY_PORT:-3001}:3001' + - '127.0.0.1:${RELAY_PORT:-31001}:3001' environment: <<: *storage-env NODE_ENV: production PORT: 3000 - SITE_URL: ${SITE_URL:-http://cbk.localhost:3000} + SITE_URL: *site-url STATIC_URL: ${STATIC_URL:-} API_URL: ${API_URL:-} HOSTS_CONFIG: ${HOSTS_CONFIG:-} - NEXTAUTH_URL: ${NEXTAUTH_URL:-http://cbk.localhost:3000} + # @note sign-in callbacks land on the site itself unless moved + NEXTAUTH_URL: ${NEXTAUTH_URL:-${SITE_URL:-http://${PLATFORM_HOST:-cbk.localhost}:${PLATFORM_PORT:-31000}}} # @note an explicitly empty value restores ordinary email sign-in NEXTAUTH_TRUSTED_SIGNIN: ${NEXTAUTH_TRUSTED_SIGNIN-true} # @note realtime channels (voice, avatars) meet at a relay the platform @@ -78,16 +147,16 @@ services: # browser dial RELAY_URL, so loopback serves both; a browser elsewhere # 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 host routing reads its 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} + RELAY_URL: *relay-url + # @note host routing reads its settings at server startup; recreate the + # container to change domains without rebuilding the image. See x-cbk + SPACE_APEX: *space-apex + PORTAL_APEX: *portal-apex APP_APEX: ${APP_APEX:-} PARTNERS_APEX: ${PARTNERS_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} + APP_MAIN_ORIGIN: *apps-url + APP_LABS_ORIGIN: *labs-url # @note left empty, the image generates these secrets on first boot and # persists them in the platform-data volume; set explicitly to override NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-} @@ -185,7 +254,7 @@ services: ports: # @note the local browser uses presigned URLs (see x-storage-env); # containers reach the same port through the Docker network alias - - '127.0.0.1:${STORAGE_PORT:-3900}:${STORAGE_PORT:-3900}' + - '127.0.0.1:${STORAGE_PORT:-31900}:${STORAGE_PORT:-31900}' networks: default: aliases: @@ -215,7 +284,7 @@ services: environment: GARAGE_ADMIN_URL: http://garage:3903 GARAGE_ADMIN_TOKEN: ${GARAGE_ADMIN_TOKEN:-dev-admin-token} - GARAGE_S3_URL: http://garage:${STORAGE_PORT:-3900} + GARAGE_S3_URL: http://garage:${STORAGE_PORT:-31900} # @note origins allowed to use presigned URLs from a browser; the URLs # themselves are the access control STORAGE_CORS_ORIGINS: ${STORAGE_CORS_ORIGINS:-*} @@ -258,7 +327,7 @@ configs: # @note the region is part of every SigV4 signature: STORAGE_REGION # must match it, or every request fails authentication s3_region = "garage" - api_bind_addr = "[::]:${STORAGE_PORT:-3900}" + api_bind_addr = "[::]:${STORAGE_PORT:-31900}" root_domain = ".s3.garage.localhost" [admin] diff --git a/docs/configuration.md b/docs/configuration.md index c1ce88c..7b4c966 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -251,7 +251,8 @@ APP_LABS_ORIGIN=https://labs.example.com 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, +`APP_LABS_ORIGIN=http://cbk-labs.localhost:3000`, with the port following +`PLATFORM_PORT`. 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. diff --git a/docs/deployment.md b/docs/deployment.md index 1f6cd32..ecfaba6 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -131,32 +131,74 @@ database it protects. The stack declares no project name, so Compose derives one. Two instances started from the same artifact would share that project - and with it the `platform-data` volume - and both would try to publish port 3000. Give each -instance its own project with `-p` and move its published ports with an -override file; the override applies after the artifact, so pass both, in that -order, on every command for that instance: - -```yaml -# staging.yml -services: - platform: - ports: !override - - '3001:3000' +instance its own project with `-p` and move its published ports through the +port variables: `PLATFORM_PORT` for the application (the app shells, spaces +and portals follow it), `RELAY_PORT` for the realtime relay and +`STORAGE_PORT` for the object store. Every derived address - `SITE_URL`, +`NEXTAUTH_URL`, the app shell origins, `RELAY_URL`, `STORAGE_URL` - picks up +the new port, so nothing else needs setting. Pass the variables in the shell +or through `--env-file` on every command for that instance, since a single +`.env` in the working directory cannot describe both: + +```bash +PLATFORM_PORT=3100 RELAY_PORT=3101 STORAGE_PORT=3901 docker compose -p cbk-staging \ + -f oci://ghcr.io/chatbotkit/platform-community:latest up -d +PLATFORM_PORT=3100 RELAY_PORT=3101 STORAGE_PORT=3901 docker compose -p cbk-staging \ + -f oci://ghcr.io/chatbotkit/platform-community:latest logs platform ``` +Volumes, networks and container names are all prefixed with the project name, +so each instance keeps its own database, generated secrets, object store and +vector index, and `-p` is also how `logs`, `ps` and `down` find the right one. + +### Endpoint manifest + +Where a stack answers is not fixed, so it says so itself. Both Compose files +carry an `x-cbk` block that lists every address something outside the Compose +network dials - the site, the Apps and Labs shells, the realtime relay, the +object store, and the space and portal wildcard apexes - with the service that +answers, the host port it is published on and the variable that overrides it. +The `url` values are the same expressions the services receive, so resolving +the file resolves the manifest against the same environment: + ```bash -docker compose -p cbk-staging \ - -f oci://ghcr.io/chatbotkit/platform-community:latest -f staging.yml up -d -docker compose -p cbk-staging \ - -f oci://ghcr.io/chatbotkit/platform-community:latest -f staging.yml logs platform +PLATFORM_HOST=studio.localhost \ + docker compose -f oci://ghcr.io/chatbotkit/platform-studio:latest \ + config --format json | jq '."x-cbk"' +``` + +```json +{ + "version": 1, + "endpoints": { + "site": { + "service": "platform", + "published": "31000", + "variable": "SITE_URL", + "url": "http://studio.localhost:31000" + }, + "apps": { "url": "http://cbk-apps.localhost:31000", "...": "..." }, + "labs": { "url": "http://cbk-labs.localhost:31000", "...": "..." }, + "relay": { "url": "http://cbk-relay.localhost:31001", "...": "..." }, + "storage": { "url": "http://cbk-storage.localhost:31900", "...": "..." } + }, + "apexes": { + "space": { "apex": "cbk-space.localhost", "published": "31000", "...": "..." }, + "portal": { "apex": "cbk-portal.localhost", "published": "31000", "...": "..." } + } +} ``` -Set `SITE_URL` and `NEXTAUTH_URL` to the instance's published address -(`http://localhost:3001` here) and `STORAGE_PORT` to a free store port -(`3901`) - in the shell or through `--env-file`, since a -single `.env` in the working directory cannot describe both instances. Volumes, -networks and container names are all prefixed with the project name, so each -instance keeps its own database, generated secrets, object store and vector -index, and `-p` is also how `logs`, `ps` and `down` find the right one. +A launcher such as ChatBotKit Studio therefore never assumes a port: it +keeps the flavor's defaults or sets `PLATFORM_PORT`, `RELAY_PORT` and +`STORAGE_PORT` to free ones (and `PLATFORM_HOST` or any endpoint's variable to +rename a host), reads the resolved manifest back and forwards, opens and trusts exactly the addresses +it lists - including the wildcard apexes, which answer on the site's port. A +bundle can move or rename its endpoints without a launcher release, since the +manifest travels inside the published artifact. Plain Compose ignores `x-` +keys, and the `version` field changes when an entry changes shape. The +manifest is data from the bundle: a launcher validates it - loopback hosts +only for a desktop install - before dialing anything it names. The artifact is published from [docker/distro/community/compose.yml](../docker/distro/community/compose.yml), @@ -166,8 +208,8 @@ under `docker/distro/`; a future PostgreSQL flavor publishes as matching image flavor. Browser-facing file upload and download flows presign URLs against the -in-stack store, published on port 3900 (`STORAGE_PORT`) under one name, -`http://cbk-storage.localhost:3900`: a `*.localhost` name browsers resolve to +in-stack store, published on port 3900 (`STORAGE_PORT`; 31900 in Studio) under +one name, `http://cbk-storage.localhost:3900`: a `*.localhost` name browsers resolve to loopback like the relay and app shells, and an alias of the `garage` service inside the Compose network, since the application fetches the same URLs. Set `STORAGE_URL` to an address both browsers and the containers can reach (with @@ -203,6 +245,13 @@ as Community. Once published from `main`, run it with: docker compose -f oci://ghcr.io/chatbotkit/platform-studio:latest up ``` +Studio publishes on its own port family - `31000` for the application, +`31001` for the relay and `31900` for the object store - so it runs beside +whatever a developer already has on Community's `3000`, `3001` and `3900`, and +the two stacks can share a host. Only the published side differs; the +containers keep their ports. The Studio app learns where the stack answers +from its [endpoint manifest](#endpoint-manifest) rather than a fixed port. + A PostgreSQL flavor would swap the database column only; the other services travel unchanged. @@ -214,8 +263,8 @@ without a purpose. `NEXTAUTH_TRUSTED_SIGNIN=true` replaces it: the sign-in page asks for an email address and signs straight into that account, creating it on first use. Sessions, audit records and the allowed-email checks are the same as after a verified code. Studio enables this mode by default and binds -its application, relay and storage ports to `127.0.0.1`. Community keeps -ordinary email sign-in. +its application, relay and storage ports (`31000`, `31001`, `31900`) to +`127.0.0.1`. Community keeps ordinary email sign-in. It is exactly as unsafe as it sounds. Anyone who can reach the port can sign in as anyone, including whoever holds the administrator addresses. So the process @@ -289,7 +338,8 @@ topology: `SITE_URL=http://cbk.localhost:3000`, with no external zones. 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`. +`APP_LABS_ORIGIN`; the port in all three follows `PLATFORM_PORT` and the +site's hostname `PLATFORM_HOST` (see [Endpoint manifest](#endpoint-manifest)). Browsers resolve any `*.localhost` name to loopback, so a space site published as `acme` answers at `http://acme.cbk-space.localhost:3000` with no DNS or hosts-file setup (`curl` needs `--resolve`). The Community and Studio Compose diff --git a/package.json b/package.json index 742031c..fbd3244 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "platform", - "version": "0.2.2", + "version": "0.3.0", "private": true, "license": "Apache-2.0", "packageManager": "pnpm@11.24.0",