Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Light Portal Install

Purpose

light-portal-install provides a one-command local installation path for Light Portal. The target user should only need Docker Compose on the host machine and should not need to clone the individual service repositories, build Java or Rust projects, install Node.js, or manually copy static assets.

The intended entrypoint is:

curl -sL https://raw.githubusercontent.com/networknt/light-portal-install/main/install.sh | bash

The installer downloads the install bundle, prepares the local data directory, writes the selected image and asset versions, and starts the stack with Docker Compose.

Recommendation

This approach should work if the repo owns the local installation contract instead of acting as a thin pointer to the current developer checkout.

The install repo contains:

  • install.sh, the idempotent installer and updater.
  • docker-compose.yml, the default local stack using the Rust services from all-in-lt.
  • .env.example, the documented image tags, ports, and optional secrets.
  • VERSION, the default portal bundle version.
  • fixed R2 archive names for hybrid-command, hybrid-query, lightapi, and signin assets.
  • README.md, the short public usage guide.

The repo should not require the user to clone portal-config-loc, service-asset, portal-view, login-view, or any service source repository. Those repos remain build and release inputs. light-portal-install consumes released images and released asset bundles.

Runtime Shape

The first version should be a Rust-only all-in-lt stack:

  • postgres
  • config-server
  • light-oauth
  • controller
  • portal-service
  • hybrid-command
  • hybrid-query
  • light-workflow
  • light-gateway
  • light-agent
  • demo customer profile API
  • demo offer decision API

The Compose service names should stay compatible with the current local stack names: controller, config-server, light-oauth, portal-service, light-gateway, hybrid-command, and hybrid-query. Internal URLs and existing bootstrap data already depend on those names.

light-agent and the demo APIs are part of the default local stack, not optional add-ons, because the install repo is meant to support a complete local demo. The Compose file should include an AI agent service based on the released networknt/light-agent image and wire it to PostgreSQL, controller, config-server, hybrid-query, and light-gateway the same way the current all-in-lt Rust profile does.

The default host entrypoint should be the gateway:

https://localhost

If binding to host port 443 fails, the installer should fall back to a documented high port such as 8443 and write the chosen value to .env.

Compose Design

docker-compose.yml should be self-contained for a released local install. It can preserve the current all-in-lt service topology, but it should not require local source-tree mounts for application jars, Rust config folders, SPA dist folders, or seed SQL unless the installer downloads them first.

The Compose file should use image variables with released defaults:

services:
  config-server:
    image: ${CONFIG_SERVER_IMAGE:-networknt/config-server:2.3.5}

  light-oauth:
    image: ${LIGHT_OAUTH_IMAGE:-networknt/light-oauth:2.3.5}

  controller:
    image: ${CONTROLLER_RS_IMAGE:-networknt/controller-rs:2.3.5}

  portal-service:
    image: ${PORTAL_SERVICE_IMAGE:-networknt/portal-service:2.3.5}

  light-gateway:
    image: ${LIGHT_GATEWAY_IMAGE:-networknt/light-gateway:2.3.5}

  light-agent:
    image: ${LIGHT_AGENT_IMAGE:-networknt/light-agent:2.3.5}

  demo-customer-profile-api:
    image: ${DEMO_CUSTOMER_PROFILE_API_IMAGE:-networknt/demo-customer-profile-api:2.3.5}

  demo-offer-decision-api:
    image: ${DEMO_OFFER_DECISION_API_IMAGE:-networknt/demo-offer-decision-api:2.3.5}

The image list is generated by release-docker-images.sh. With --upload-r2, the script uploads docker-images.env to:

light-portal/releases/<tag>/docker-images.env
light-portal/releases/latest/docker-images.env
docker-images.env

For public installs, install.sh downloads docker-images.env for the selected LIGHT_PORTAL_VERSION and runs Compose with both the downloaded image env file and the local .env overrides:

docker compose --env-file docker-images.env --env-file .env up -d

The Compose file should keep persistent state in named volumes by default:

  • postgres-data for PostgreSQL.
  • portal-data for user-uploaded or generated portal data.

The installer may support a --dev-bind-mounts option later, but the default public path should prefer downloaded, immutable release assets over bind mounts to a developer workspace.

Local Authentication

The local install should use the same OAuth authorization code flow through login-view that is used by the current portal-config-loc/all-in-lt and portal-config-dev deployments. The gateway serves both the portal UI and the sign-in UI, and light-oauth remains the local authorization server.

To keep the first public local install simple, the bundle should continue using the existing long-lived local demo tokens already used by the current local stack. Token generation can be revisited later, but it should not block the initial installer.

Installer Flow

install.sh should be idempotent and safe to rerun.

  1. Detect docker compose.
  2. Resolve the requested version. Default to the repo VERSION; allow LIGHT_PORTAL_VERSION=....
  3. Create an install directory, defaulting to $HOME/.light-portal.
  4. Download docker-images.env from light-portal/releases/<version>/docker-images.env.
  5. Download hybrid-command.zip, hybrid-query.zip, lightapi.zip, signin.zip, and events.zip from R2.
  6. Use the checked-in bootstrap config, seed SQL, certificates, and Compose file.
  7. Preserve existing .env values when updating.
  8. Start the stack with docker compose up -d.
  9. Wait for health checks and print the portal URL.

The script should provide explicit subcommands:

install.sh install
install.sh update
install.sh start
install.sh stop
install.sh status
install.sh logs
install.sh uninstall

uninstall should ask for confirmation before deleting volumes or $HOME/.light-portal.

Static Asset Distribution

The current service-asset GitHub repository is useful as a build artifact repository for developers, but it is not ideal as the long-term public CDN for local installers. Released static content and install scripts should move to Cloudflare R2 as the long-term artifact channel, with fixed archive objects, checksums, and rollback.

update-asset.sh --upload-r2 currently uploads refreshed service assets to bucket lightapi. Directory assets are compressed before upload. The default object paths are:

hybrid-command.zip
hybrid-query.zip
lightapi.zip
signin.zip
events.zip

release-docker-images.sh --upload-r2 uploads the image env file under:

light-portal/releases/<tag>/docker-images.env
light-portal/releases/latest/docker-images.env
docker-images.env

daily-release.sh is the top-level release entrypoint. It calls update-asset.sh --upload-r2 first, then runs the dev copy steps, then calls release-docker-images.sh --upload-r2.

The install repo should treat R2 as an artifact origin, not as the source of truth. Source of truth remains the service and UI repos plus the release pipeline. The release pipeline publishes immutable versioned objects to R2.

Once the static bundles, scripts, generated manifests, and release asset publishing are fully moved to R2, the service-asset repository can be removed. During migration, it can remain as a staging or compatibility source until the install pipeline no longer depends on it.

Current object layout:

hybrid-command.zip
hybrid-query.zip
lightapi.zip
signin.zip
events.zip
docker-images.env
light-portal/releases/
  <tag>/
    docker-images.env
  latest/
    docker-images.env

The installer can default to latest for daily local demo installs, while still allowing LIGHT_PORTAL_VERSION=<tag> for reproducible installs.

Because the installer should not depend on AWS CLI access, it cannot use aws s3 ls to discover R2 objects. It downloads the known compressed archives directly with curl and unpacks them with unzip into the Docker Compose bind-mount directories.

R2 Tradeoffs

R2 is attractive because it supports S3-compatible tooling, public buckets, custom domains, caching through Cloudflare, and no R2 egress bandwidth charges. Cloudflare documents Standard storage pricing, request-class pricing, a free tier, and free egress for R2. Cloudflare also documents that public buckets can be exposed through custom domains for production use, while r2.dev public URLs are intended for non-production traffic.

The main tradeoff is that heavy asset reads still have request-operation cost. The current implementation publishes five compressed archives for hybrid-command, hybrid-query, lightapi, signin, and events.json, plus docker-images.env, to keep install downloads coarse-grained.

For production-quality public distribution, use a custom domain such as:

https://assets.lightapi.net/light-portal/releases/2.3.5/manifest.json

Do not use an r2.dev URL as the documented installer default.

Release Pipeline

The current release pipeline produces one installable daily version with these steps:

  1. daily-release.sh calls update-asset.sh --upload-r2.
  2. update-asset.sh rebuilds/copies portal-view, login-view, hybrid service jars, and events.json.
  3. update-asset.sh replaces the configured R2 asset prefixes in bucket lightapi.
  4. daily-release.sh runs copy-service-dev.sh and copy-site-dev.sh for dev.
  5. daily-release.sh calls release-docker-images.sh --upload-r2.
  6. release-docker-images.sh builds/pushes the selected image profile and writes docker-images.env.
  7. release-docker-images.sh uploads docker-images.env to the versioned, latest, and compatibility R2 paths.

The next release-pipeline improvement should generate and upload:

  • checksums for each archive and metadata file
  • an install smoke-test result

The smoke test should run from an empty install directory and verify:

  • Docker Compose starts all required services.
  • PostgreSQL bootstrap completes.
  • gateway is reachable.
  • sign-in and portal static assets load through gateway.
  • health checks pass for the Rust services.

Settled Decisions

  • Include light-agent in the default docker-compose.yml as the AI agent service for local demos.
  • Include the demo customer profile and offer decision APIs in the default local stack.
  • Use the local OAuth authorization code flow with login-view, matching portal-config-loc/all-in-lt and portal-config-dev.
  • Use the existing long-lived local demo tokens in the first version to keep the installer simple.
  • Move static content, install scripts, generated manifests, and release bundles to Cloudflare R2 for long-term flexibility.
  • Remove the service-asset repository after the R2-based release pipeline fully replaces it.
  • Keep Docker Compose as the only container runtime dependency; use curl for R2 downloads, not AWS CLI.
  • Use fixed archive names for the current R2 object set until the release pipeline publishes a richer manifest automatically.

Decision

Create light-portal-install as the public local install repo with Docker Compose as the only container runtime dependency. Use the Rust all-in-lt service topology, including light-agent and the demo APIs, but package it as checked-in runtime config plus R2-downloaded service jars, SPA assets, events.json, and docker-images.env. Keep the local OAuth authorization code flow through login-view and use existing long-lived local demo tokens for the first version. Move static install artifacts from GitHub repo distribution to Cloudflare R2 behind a custom domain, download the fixed asset archives with curl, extract them with unzip, and remove service-asset after the R2 pipeline fully replaces it.