Skip to content

Add Docker Compose deployment workflow#65

Open
wangwanjie wants to merge 16 commits intonexu-io:mainfrom
wangwanjie:feature/docker-compose-deploy
Open

Add Docker Compose deployment workflow#65
wangwanjie wants to merge 16 commits intonexu-io:mainfrom
wangwanjie:feature/docker-compose-deploy

Conversation

@wangwanjie
Copy link
Copy Markdown

Summary

  • Add a single Alpine-based Docker runtime image for Open Design.
  • Add Docker Compose deployment with persistent /app/.od storage, health checks, memory limits, and .env.example defaults.
  • Add Docker Hub publishing and verification scripts with multi-arch support, proxy detection, skopeo push support, and manifest checks.

Test Plan

  • bash -n deploy/scripts/publish-images.sh && bash -n deploy/scripts/verify-image.sh && bash -n deploy/scripts/verify-image-manifest.sh
  • docker compose --env-file deploy/.env.example -f deploy/docker-compose.yml config
  • deploy/scripts/publish-images.sh --dry_run --arch arm64 --image_tag pr-check
  • git diff --check
  • docker buildx build --platform linux/arm64 -t open-design:pr-check -f deploy/Dockerfile --load .
  • deploy/scripts/verify-image.sh open-design:pr-check

Published Image

  • docker.io/vanjayak/open-design:latest
  • Manifest includes linux/amd64 and linux/arm64.

@lefarcen lefarcen self-requested a review April 29, 2026 10:38
@lefarcen lefarcen added the enhancement New feature or request label Apr 29, 2026
@lefarcen
Copy link
Copy Markdown
Contributor

Hi @wangwanjie! 🎉

Thanks for this comprehensive Docker deployment contribution — great to see a production-ready Alpine runtime with multi-arch support, health checks, and memory limits built in!

I will run a deep review across code correctness + deployment design and get back to you within 24h.

Join our community while you wait:

Thanks for making open-design better!
— open-design team

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc7d4ee173

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

image_sources=()

for platform in "${platform_list[@]}"; do
ensure_base_images_preloaded "$platform"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate base-image preloading by push strategy

The main loop always calls ensure_base_images_preloaded before each build, and that helper always invokes skopeo copy when PRELOAD_BASE_IMAGES=1. This makes --push_strategy buildx fail on hosts without skopeo, even though the script advertises buildx as an alternative push path. In practice, users selecting buildx to avoid skopeo still hit a hard failure before any build starts.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already handled in the current script: ensure_base_images_preloaded calls should_use_preloaded_base_images first, and that helper only returns true when PRELOAD_BASE_IMAGES=1 and PUSH_STRATEGY=skopeo. With --push_strategy buildx, the preload path returns before requiring or invoking skopeo.

Comment thread deploy/scripts/publish-images.sh Outdated
[[ "$INSPECT_AFTER_PUSH" == "1" ]] || return 0

if [[ "$PUSH_STRATEGY" == "skopeo" ]]; then
skopeo inspect --raw "docker://${image}" >/dev/null
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reuse authfile for post-push skopeo inspection

After a successful skopeo push, the script validates the remote image with skopeo inspect --raw but does not pass --authfile. When credentials come from credsStore (and are materialized into EFFECTIVE_SKOPEO_AUTHFILE earlier), private repositories can push successfully and then fail at inspection with an auth error, causing false-negative publish failures.

Useful? React with 👍 / 👎.

Comment thread deploy/scripts/verify-image-manifest.sh Outdated
os="${platform%/*}"
arch="${platform#*/}"
if ! jq -e --arg os "$os" --arg arch "$arch" '
.mediaType == "application/vnd.docker.distribution.manifest.list.v2+json" and
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept OCI index manifests in platform verifier

The manifest verifier hard-requires Docker manifest-list media type before checking platform entries. Valid multi-arch images published as OCI image indexes are therefore reported as missing platforms even when the manifests[] entries are correct, which can block release verification for compliant OCI images.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@lefarcen lefarcen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @wangwanjie, thanks for the PR! 🎉 This is a solid foundation for Docker deployment — Alpine multi-stage build, multi-arch support, memory limits, health checks, and thorough verification scripts. The structure is clean and the test plan is comprehensive.

Found 5 P1 security/correctness issues that need fixes before merge, plus several P2/P3 improvements. The core concerns:

  1. Shell injection risk in publish-images.sh (unquoted user-supplied variables)
  2. Docker credential exposure via credential helpers (cleartext secrets in temp file)
  3. TOCTOU race in temp file handling
  4. Missing input validation on critical paths
  5. Container escape risk via --add-host in untrusted environments

P1 Issues (must fix)

deploy/scripts/publish-images.sh:218 - Credential exposure via cleartext temp file

When Docker uses a credential helper, this code extracts the secret and writes it to a temp file in cleartext. If the process crashes before cleanup, the temp file persists with credentials readable by any user.

Fix: Use mktemp --tmpdir=/dev/shm/... (memory-backed) + chmod 600 immediately.

deploy/scripts/publish-images.sh:99 - Shell injection via unquoted variable

proxy_url (from HTTP_PROXY env) is passed unquoted into Perl. Add input validation: [[ ! "$proxy_url" =~ ^https?://[a-zA-Z0-9._-]+(:[0-9]+)?$ ]]

deploy/scripts/publish-images.sh:240 - TOCTOU race in temp directory

Use rm -rfP to avoid symlink attacks, or verify directory before deletion.

deploy/scripts/publish-images.sh:322 - Missing input validation on --image_namespace/--image_repository

Validate: [[ ! "$IMAGE_NAMESPACE" =~ ^[a-zA-Z0-9_-]+$ ]] to prevent path traversal in image refs.

deploy/scripts/publish-images.sh:319 - Container escape risk via --add-host

--add-host host.docker.internal=host-gateway exposes host network. Make opt-in or document security assumption.

P2 Issues (recommended)

  • Dockerfile:34 - Native module stripping may break better-sqlite3. Either test after stripping or skip .node files.
  • docker-compose.yml:20 - Add security_opt: no-new-privileges + read_only: true for defense in depth.
  • .env.example:9 - Document tested memory workloads (384m may OOM on large exports).

P3 Improvements

  • server.js:1087 - IPv6 :: bind shows misleading 'localhost' URL
  • README.md:23 - Add guidance on agent CLI installation (volume mount vs derived image)

Most P1s can be fixed with input validation + proper quoting. Let me know if you need clarification on any of these!

Harden publishing inputs and temporary credential handling, and tighten Docker runtime defaults requested by the PR review.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@wangwanjie
Copy link
Copy Markdown
Author

Thanks for the detailed review. I've pushed fixes for the reported security/correctness issues:

  • Added proxy URL and image namespace/repository validation in publish-images.sh.
  • Removed unconditional host.docker.internal=host-gateway; it is now only added when a local proxy rewrite is needed.
  • Hardened temporary credential/authfile handling with private temp roots, chmod 600, and centralized cleanup.
  • Reused the effective skopeo authfile for post-push inspection.
  • Gated base-image preloading to the skopeo strategy so --push_strategy buildx no longer requires skopeo preloading.
  • Accepted OCI image indexes in the manifest verifier.
  • Added Compose hardening with read_only, /tmp tmpfs, and no-new-privileges.
  • Removed native .node stripping from the Dockerfile and fixed IPv6 listen URL formatting.

Validation run:

  • bash -n deploy/scripts/publish-images.sh && bash -n deploy/scripts/verify-image-manifest.sh
  • git diff --check
  • dry-run checks for skopeo/buildx, proxy validation, image namespace/repository validation
  • docker compose --env-file deploy/.env.example -f deploy/docker-compose.yml config
  • node --check daemon/server.js

I could not run pnpm typecheck locally because this checkout has no node_modules, and the current local Node version is v25 while the project engine requires >=20 <23.

Copy link
Copy Markdown
Contributor

@lefarcen lefarcen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work, @wangwanjie! 🎉 All P1 security issues have been fixed comprehensively:

Shell injection - Proxy URL validation with regex checks
Credential exposure - Memory-backed temp roots ( or 700-perms fallback) + on authfiles
TOCTOU race - Centralized temp dir management with cleanup validation
Input validation - Namespace/repository validation with Docker name component regex
Container escape - now conditional (only when proxy rewrite needed)

P2/P3 fixes:

  • Native .node stripping removed from Dockerfile
  • IPv6 listen host formatting ( for IPv6 any)
  • Compose hardening: read_only: true, tmpfs, no-new-privileges
  • Skopeo auth reused for post-push inspection
  • Base-image preloading gated to skopeo strategy

The implementation is solid — temp file handling is now defense-in-depth (private roots + restrictive perms + centralized cleanup), input validation is thorough, and the Compose runtime is properly hardened.

Remaining optional improvements (P3, non-blocking):

  1. deploy/.env.example memory docs (P2 → P3 after your validation work) — Consider adding a workload note like:

  2. deploy/README.md agent CLI installation — Add the options subsection I suggested (volume-mount vs derived image)

But these are polish — the core security/correctness is now excellent. Approving! 🚀

Copy link
Copy Markdown
Contributor

@lefarcen lefarcen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work, @wangwanjie! 🎉 All P1 security issues have been fixed comprehensively:

Shell injection - Proxy URL validation with regex checks
Credential exposure - Memory-backed temp roots (/dev/shm or 700-perms fallback) + chmod 600 on authfiles
TOCTOU race - Centralized temp dir management with cleanup validation
Input validation - Namespace/repository validation with Docker name component regex
Container escape - --add-host now conditional (only when proxy rewrite needed)

P2/P3 fixes:

  • Native .node stripping removed from Dockerfile
  • IPv6 listen host formatting for :: bind
  • Compose hardening: read_only, tmpfs, no-new-privileges
  • Skopeo auth reused for post-push inspection
  • Base-image preloading gated to skopeo strategy

The implementation is solid — temp file handling is now defense-in-depth (private roots + restrictive perms + centralized cleanup), input validation is thorough, and the Compose runtime is properly hardened.

Remaining optional improvements (P3, non-blocking):

  1. deploy/.env.example memory docs — Consider adding a workload note (idle ~20 MB; single 10 MB export peaks ~180 MB; concurrent agent +50 MB each → 384m supports ~3 concurrent + moderate exports).
  2. deploy/README.md agent CLI installation — Add the options subsection I suggested (volume-mount vs derived image).

But these are polish — the core security/correctness is now excellent. Approving! 🚀

lefarcen
lefarcen previously approved these changes Apr 30, 2026
Copy link
Copy Markdown
Contributor

@lefarcen lefarcen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All security issues resolved! This is ready to merge from a security/correctness perspective. The Docker deployment is production-ready. 🎉

@lefarcen
Copy link
Copy Markdown
Contributor

Hi @wangwanjie! 👋

The PR has a merge conflict that needs to be resolved before we can merge. Could you please:

git checkout main
git pull origin main
git checkout docker-deploy
git rebase main
# Resolve any conflicts
git push -f origin docker-deploy

Once the conflicts are resolved, we'll be ready to merge — the code review is already approved! ✅

@lefarcen
Copy link
Copy Markdown
Contributor

Hi @wangwanjie, your PR has a merge conflict with main. Could you rebase and resolve it? Once the conflict is fixed, we can merge immediately — all security issues have been addressed! 🚀

wangwanjie and others added 2 commits April 30, 2026 12:12
Resolve the daemon monorepo migration conflict and adapt Docker deployment files to the new workspace layout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Set CI=true during the image build so pnpm prune can run non-interactively inside Docker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread deploy/Dockerfile Outdated

FROM ${RUNTIME_IMAGE}

RUN apk add --no-cache nodejs tini && \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wangwanjie Thanks for putting this Docker workflow together — the overall direction is really useful. One thing I’d tighten before merge: the build stage uses Node 24, but the runtime stage installs nodejs from Alpine 3.22, which may not be Node 24. Since better-sqlite3 is native and built during install, running it under a different Node major/ABI can cause startup failures. Could we use a Node 24 runtime image too, or otherwise pin/install exactly Node 24 in the final stage? 🙏

Comment thread deploy/scripts/verify-image.sh Outdated
for required_path in \
"app/apps/daemon/server.js" \
"app/apps/web/out/index.html" \
"app/node_modules/better-sqlite3" \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small verification follow-up: in this pnpm workspace, better-sqlite3 is a daemon package dependency, so the direct workspace symlink should be under app/apps/daemon/node_modules/better-sqlite3 rather than app/node_modules/better-sqlite3. Updating this check would make the verifier match the actual runtime layout and avoid false failures. 😊

wangwanjie and others added 4 commits April 30, 2026 13:15
Use pnpm deploy for the daemon package so the runtime image includes production dependencies where Node resolves them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Allow pnpm v10 deploy to package the daemon workspace without requiring injected workspace packages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Use Node 24 for both build and runtime stages and update image verification for the workspace daemon dependency layout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@wangwanjie wangwanjie marked this pull request as draft April 30, 2026 07:13
@lefarcen
Copy link
Copy Markdown
Contributor

lefarcen commented May 2, 2026

Hi @wangwanjie!

The bot review on this PR is positive overall, but there are now merge conflicts with main (about 44h since the last update).

Could you rebase against latest main? If you'd like a hand walking through the rebase, just reply here. Once conflicts are resolved, a maintainer can take a final look and merge.

If you've moved on, no worries — feel free to close.

Thanks for the contribution!
— open-design team

Adapt Docker packaging to the latest workspace layout and prevent single-arch publishes from overwriting the multi-arch latest tag.
@wangwanjie
Copy link
Copy Markdown
Author

Updated this PR with the latest main merge and Docker Compose deployment fixes.

Changes included:

  • Adapted the Docker build to the current workspace layout (packages/, tools/, daemon dist/cli.js, new runtime resource directories).
  • Added OD_BIND_HOST / OD_ALLOWED_ORIGINS support for reverse-proxy and public-domain Docker deployments.
  • Fixed publish-images.sh --arch <arch> so single-arch publishes go to <tag>-<arch> and no longer overwrite the multi-arch latest manifest.
  • Documented digest pinning and allowed-origin deployment settings.

Validation completed:

  • pnpm --filter @open-design/daemon build
  • pnpm --filter @open-design/daemon exec vitest run tests/mcp-install-info.test.ts tests/origin-validation.test.ts
  • pnpm --filter @open-design/web build
  • pnpm --filter @open-design/desktop build && pnpm typecheck
  • Docker publish script dry-runs for single-arch and multi-arch flows
  • Published and verified docker.io/vanjayak/open-design:latest as a multi-arch manifest list

Current published latest digest:

sha256:bbaa6076a9b42f7cde41283c7743a2ea8680030efacf3f9688c8881dbd631b04

The deployed Docker Compose path has also been manually verified behind a reverse proxy using OD_ALLOWED_ORIGINS.

@wangwanjie wangwanjie marked this pull request as ready for review May 5, 2026 06:08
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6a232527b0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread deploy/scripts/verify-image.sh Outdated
ARCHIVE_CONTAINER_ID=""

for required_path in \
"app/apps/daemon/server.js" \
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Verify daemon artifact under dist/ instead of repo root

The required path check expects app/apps/daemon/server.js, but this image runs from apps/daemon/dist/cli.js and the daemon package only ships dist outputs, so this assertion can fail even for a correct runtime image. That creates a false-negative in the release gate and blocks publish verification despite a bootable image.

Useful? React with 👍 / 👎.

Comment thread deploy/scripts/verify-image.sh Outdated
fi
done

runtime_tools="$(docker run --rm --entrypoint sh "$IMAGE_REF" -lc 'for tool in python3 g++ make npm pnpm; do if command -v "$tool" >/dev/null 2>&1; then echo "$tool"; fi; done')"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove npm from forbidden runtime tool list

The runtime tool check treats npm as disallowed, but the chosen runtime base (node:24-alpine) includes npm by default, so this condition will fail for normal images and turn verification into a permanent false alarm. As written, valid builds are rejected before health checks complete.

Useful? React with 👍 / 👎.

Use OD_BIND_HOST as the single daemon bind-host setting for Docker deployment and origin validation.
@wangwanjie
Copy link
Copy Markdown
Author

Follow-up update: removed the legacy OD_HOST compatibility path per deployment feedback.

OD_BIND_HOST is now the single daemon bind-host setting used by:

  • the CLI default host resolution
  • server-side same-origin checks
  • Dockerfile runtime defaults
  • Docker Compose environment

Validation rerun:

  • pnpm --filter @open-design/daemon build
  • pnpm --filter @open-design/daemon exec vitest run tests/mcp-install-info.test.ts tests/origin-validation.test.ts
  • bash -n deploy/scripts/publish-images.sh
  • deploy/scripts/publish-images.sh --arch arm64 --dry_run
  • git diff --check

Copy link
Copy Markdown
Contributor

@lefarcen lefarcen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @wangwanjie, thanks for the thorough rework! 👏

All the P1 security issues from the previous review are now properly addressed:

  • ✅ Proxy URL validation with strict regex (scheme://host:port only)
  • ✅ Credential temp file handling hardened (chmod 600 + private temp roots in /dev/shm or 700-mode fallback)
  • ✅ Image namespace/repository validation (validate_image_name_component with lowercase Docker naming rules)
  • ✅ Centralized temp cleanup with whitelist patterns
  • OD_BIND_HOST / OD_ALLOWED_ORIGINS support for reverse-proxy deployments added to daemon

The Docker deployment workflow is production-ready from a security standpoint. The architecture changes (workspace layout adaptation, multi-arch manifest handling, origin validation) align well with the current main branch.

Ship when ready from my side — deferring final approval to a maintainer.

Check the packaged daemon dist entrypoint and allow npm from the Node 24 runtime image while still rejecting build-only tools.
@wangwanjie
Copy link
Copy Markdown
Author

Addressed the latest non-outdated verifier feedback:

  • deploy/scripts/verify-image.sh now checks the packaged daemon entrypoint at app/apps/daemon/dist/cli.js instead of the old repo-root server.js path.
  • The runtime tool check no longer treats npm as forbidden because node:24-alpine ships npm by default; it still rejects build-only tools (python3, g++, make, pnpm).

Validation:

  • bash -n deploy/scripts/verify-image.sh && bash -n deploy/scripts/publish-images.sh
  • git diff --check
  • deploy/scripts/verify-image.sh docker.io/vanjayak/open-design:latest-amd64

The older base-image preloading thread is already handled by should_use_preloaded_base_images, which gates preloading to PUSH_STRATEGY=skopeo.

Copy link
Copy Markdown
Contributor

@lefarcen lefarcen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verifier refinements look good — daemon entrypoint path and npm runtime check both match the current workspace layout.

All prior security findings remain fixed; this commit only improves the verification script itself. Ship-ready from my side — deferring final approval to a maintainer.

Copy link
Copy Markdown
Contributor

@lefarcen lefarcen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @wangwanjie, reviewed the private LAN origin support — the core security logic looks solid! 🔒

What's good:

  • IP range checks are correct per RFC 1918 + link-local (10.x, 172.16-31.x, 192.168.x, 169.254.x)
  • The isAllowedBrowserOrigin hostname matching (parsedOrigin.hostname === requestHost.hostname) correctly prevents DNS rebinding attacks when the browser is on a private LAN IP
  • The new test cases cover the critical security boundary (Origin≠Host rejection)

Suggestions (non-blocking):

  1. Test coverage gaps (P2): Only 192.168.x.x is tested. Consider adding test cases for:
    • 10.x.x.x range
    • 172.16-31.x.x range (and boundary rejection: 172.15.x.x / 172.32.x.x)
    • 169.254.x.x link-local
    • Malformed IPs (e.g., "192.168.1.256" should be rejected)
  2. Code duplication (P3): The validation helpers are duplicated in both server.ts and the test file. Future maintenance risk if production logic changes but test helpers don't. Consider extracting to a shared module or importing from server.ts in tests (if test isolation allows).

Verdict: Ship-ready from security/correctness perspective. The suggestions above are quality-of-life improvements for future maintainability.

Thanks for the clean refactor and thorough test coverage! 🙌

Move browser origin validation into a shared daemon module so tests exercise the production logic and cover the remaining private LAN edge cases.
@wangwanjie
Copy link
Copy Markdown
Author

Addressed the latest Suggestions in 4472f8b:

  • Extracted daemon browser origin validation into apps/daemon/src/origin-validation.ts so production code and tests share the same helper logic.
  • Updated origin guard tests to import the shared helpers instead of duplicating validation code.
  • Added private LAN coverage for 10.x.x.x, 172.16.x.x, 172.31.x.x, and 169.254.x.x.
  • Added rejection coverage for 172.15.x.x, 172.32.x.x, and malformed 192.168.1.256.

Validation:

  • pnpm --filter @open-design/daemon exec vitest run tests/origin-validation.test.ts tests/mcp-install-info.test.ts tests/app-config.test.ts
  • pnpm --filter @open-design/daemon typecheck
  • git diff --check
  • pnpm --filter @open-design/daemon test (34 files / 495 tests passed)

The PR branch has been pushed. Docker image publishing is running now; I will follow up with the new digest once it completes.

@wangwanjie
Copy link
Copy Markdown
Author

Docker publish follow-up:

Published and verified docker.io/vanjayak/open-design:latest.

Current latest manifest digest:

sha256:bef1018f7b7835c5b34681f24d2b808e001c56722f82b355bbc1f80f293ac82b

Validation:

  • deploy/scripts/publish-images.sh --image_tag latest
  • deploy/scripts/verify-image-manifest.sh docker.io/vanjayak/open-design:latest
  • skopeo inspect --raw docker://docker.io/vanjayak/open-design:latest | shasum -a 256

Copy link
Copy Markdown
Contributor

@mrcfps mrcfps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wangwanjie thanks for the continued Docker deployment work and for addressing the earlier review feedback 🙏. I found two blockers in the Compose deployment path: the default published port exposes the unauthenticated daemon beyond localhost, and the documented host-port override currently breaks same-origin browser API calls. Details are inline.

Generated by Looper 0.6.1 · runner=reviewer · agent=opencode

Comment thread deploy/docker-compose.yml Outdated
OD_ALLOWED_ORIGINS: ${OPEN_DESIGN_ALLOWED_ORIGINS:-}
OD_PORT: 7456
ports:
- "${OPEN_DESIGN_PORT:-7456}:7456"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This default port mapping publishes the daemon on every host interface. The new docs tell users to set OPEN_DESIGN_ALLOWED_ORIGINS for public-IP/domain/reverse-proxy deployments, but that is only a browser Origin filter: the daemon still allows requests with no Origin header through the /api middleware, so any curl/server-side client that can reach this port can call the unauthenticated API. That makes the default Compose deployment unsafe on machines with a public or shared LAN interface. Please bind localhost by default, e.g. 127.0.0.1:${OPEN_DESIGN_PORT:-7456}:7456, and document putting an authenticated reverse proxy/VPN in front before exposing it remotely (or add real daemon auth for deployed mode).

Comment thread deploy/docker-compose.yml
NODE_OPTIONS: ${NODE_OPTIONS:---max-old-space-size=192}
OD_BIND_HOST: 0.0.0.0
OD_ALLOWED_ORIGINS: ${OPEN_DESIGN_ALLOWED_ORIGINS:-}
OD_PORT: 7456
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OPEN_DESIGN_PORT can publish the service on a different external port, but the container only advertises OD_PORT=7456 to the daemon. The origin checks only accept OD_PORT (plus OD_WEB_PORT), so the documented OPEN_DESIGN_PORT=8080 override makes browser requests arrive with Host/Origin on :8080 and get rejected as cross-origin even though the page is same-origin from the browser's perspective. Please pass the published port into the allowed-port set, for example by adding OD_WEB_PORT: ${OPEN_DESIGN_PORT:-7456} in this Compose environment (or by otherwise deriving the external origin/port for the daemon).

wangwanjie added 2 commits May 7, 2026 02:15
Bind the Compose deployment to localhost by default and pass the published port through to the daemon origin checks so host-port overrides remain same-origin.
@wangwanjie
Copy link
Copy Markdown
Author

Addressed the latest Compose deployment blockers and pushed the branch.

Changes:

  • Default Compose port binding is now localhost-only: 127.0.0.1:${OPEN_DESIGN_PORT:-7456}:7456, so the unauthenticated daemon is not published on every host interface by default.
  • Compose now passes OD_WEB_PORT: ${OPEN_DESIGN_PORT:-7456} so OPEN_DESIGN_PORT=8080 remains same-origin for browser API calls.
  • Updated deploy/README.md and deploy/.env.example to document localhost binding and the need for an authenticated reverse proxy, SSH tunnel, or VPN before remote exposure.
  • Merged latest gh_origin/main into this branch to avoid stale conflicts.

Commits:

  • 157deb9 Harden Docker Compose port exposure
  • f7a4eda Merge remote-tracking branch 'gh_origin/main' into feature/docker-compose-deploy

Validation:

  • docker compose --env-file deploy/.env.example -f deploy/docker-compose.yml config
  • OPEN_DESIGN_PORT=8080 docker compose --env-file deploy/.env.example -f deploy/docker-compose.yml config
  • pnpm --filter @open-design/daemon exec vitest run tests/origin-validation.test.ts tests/mcp-install-info.test.ts tests/app-config.test.ts
  • pnpm --filter @open-design/daemon typecheck
  • pnpm --filter @open-design/daemon test
  • git diff --check

mrcfps
mrcfps previously approved these changes May 6, 2026
Copy link
Copy Markdown
Contributor

@mrcfps mrcfps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wangwanjie I reviewed the current Docker Compose deployment changes, including the localhost-only port binding, OD_WEB_PORT propagation for custom published ports, shared daemon origin-validation helpers and tests, the Docker runtime image layout, and the publish/verify scripts. The previously blocking Compose exposure and host-port origin issues are addressed, and I did not find any remaining actionable correctness, safety, or maintainability concerns in the changed ranges. Thanks for the careful follow-through and validation work 🙏

Generated by Looper 0.6.1 · runner=reviewer · agent=opencode

@wangwanjie
Copy link
Copy Markdown
Author

Follow-up after merging latest main:

  • Merged latest gh_origin/main into this branch and resolved the apps/daemon/src/server.ts conflict.
  • Kept the PR's shared origin-validation helper path and integrated upstream's GET-only portless loopback Origin fallback.
  • Reviewed deploy/Dockerfile against the merged workspace (0.4.1, Node 24-alpine, daemon dist/cli.js, web out, pnpm 10.33.2). No Dockerfile changes were needed.

Validation:

  • pnpm install (refresh workspace links/generated package dist after merge)
  • pnpm --filter @open-design/daemon typecheck
  • pnpm --filter @open-design/daemon test (68 files / 1037 tests passed)
  • git diff --check
  • deploy/scripts/publish-images.sh --dry_run --image_tag latest
  • docker compose --env-file deploy/.env.example -f deploy/docker-compose.yml config
  • OPEN_DESIGN_PORT=8080 docker compose --env-file deploy/.env.example -f deploy/docker-compose.yml config

Pushed merge commit ae14186 to the PR branch.

Copy link
Copy Markdown
Contributor

@mrcfps mrcfps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wangwanjie I reviewed the current Docker Compose deployment head, including the localhost-only Compose binding, OD_WEB_PORT custom-port propagation, shared daemon origin-validation helper and coverage, the Node 24 Docker runtime layout, and the publish/verify scripts. The previously raised Compose exposure and custom-port origin issues remain addressed after the latest main merge, and I did not find any remaining actionable correctness, safety, or maintainability concerns in the changed ranges. Thanks for the careful follow-through and validation work 🙏

Generated by Looper 0.6.2 · runner=reviewer · agent=opencode

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants