@@ -647,7 +647,7 @@ RUN printf '%s\n' \
647647 'Type=simple' \
648648 'User=opencode' \
649649 'WorkingDirectory=/home/opencode/workspace' \
650- 'ExecStart=/opt/opencode/bin/opencode web --port 3000 --hostname 0.0.0.0 ' \
650+ 'ExecStart=/bin/bash -lc " OPENCODE_PORT=$${OPENCODE_PORT:-$${PORT:-3000}}; OPENCODE_HOST=$${OPENCODE_HOST:-0.0.0.0}; exec / opt/opencode/bin/opencode web --port $${OPENCODE_PORT} --hostname $${OPENCODE_HOST} " ' \
651651 'Restart=always' \
652652 'RestartSec=5' \
653653 'Environment=PATH=/opt/opencode/bin:/home/opencode/.local/bin:/home/opencode/.cargo/bin:/home/opencode/.local/share/mise/shims:/home/opencode/.bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' \
@@ -686,14 +686,115 @@ RUN ls -la /home/opencode/.config/opencode/opencode.jsonc && cat /home/opencode/
686686# Note: Entrypoint runs as root to support both modes; tini mode drops to opencode user
687687RUN printf '%s\n ' \
688688 '#!/bin/bash' \
689- 'if [ " ${USE_SYSTEMD}" = " 1" ]; then' \
689+ 'set -euo pipefail' \
690+ '' \
691+ 'log() {' \
692+ ' echo " [opencode-cloud] $*"' \
693+ '}' \
694+ '' \
695+ 'OPENCODE_PORT=" ${OPENCODE_PORT:-${PORT:-3000}}"' \
696+ 'OPENCODE_HOST=" ${OPENCODE_HOST:-0.0.0.0}"' \
697+ 'export OPENCODE_PORT OPENCODE_HOST' \
698+ '' \
699+ 'if [ " ${USE_SYSTEMD:-}" = " 1" ]; then' \
690700 ' exec /sbin/init' \
691701 'else' \
692702 ' # Ensure broker socket directory exists' \
693703 ' install -d -m 0755 /run/opencode' \
704+ '' \
705+ ' # Ensure user records directory exists (ephemeral unless mounted)' \
706+ ' install -d -m 0700 /var/lib/opencode-users' \
707+ '' \
708+ ' restore_users() {' \
709+ ' shopt -s nullglob' \
710+ ' local records=(/var/lib/opencode-users/*.json)' \
711+ ' if [ ${#records[@]} -eq 0 ]; then' \
712+ ' return 1' \
713+ ' fi' \
714+ ' for record in " ${records[@]}"; do' \
715+ ' local username password_hash locked' \
716+ ' username=" $(jq -r ".username // empty" "${record}" )"' \
717+ ' password_hash=" $(jq -r ".password_hash // empty" "${record}" )"' \
718+ ' locked=" $(jq -r ".locked // false" "${record}" )"' \
719+ ' if [ -z " ${username}" ]; then' \
720+ ' log " Skipping invalid user record: ${record}"' \
721+ ' continue' \
722+ ' fi' \
723+ ' if ! id -u " ${username}" >/dev/null 2>&1; then' \
724+ ' log " Creating user: ${username}"' \
725+ ' useradd -m -s /bin/bash " ${username}"' \
726+ ' fi' \
727+ ' if [ -n " ${password_hash}" ]; then' \
728+ ' usermod -p " ${password_hash}" " ${username}"' \
729+ ' fi' \
730+ ' if [ " ${locked}" = " true" ]; then' \
731+ ' passwd -l " ${username}" >/dev/null' \
732+ ' else' \
733+ ' passwd -u " ${username}" >/dev/null || true' \
734+ ' fi' \
735+ ' log " Restored user: ${username}"' \
736+ ' done' \
737+ ' return 0' \
738+ ' }' \
739+ '' \
740+ ' persist_user_record() {' \
741+ ' local username=" $1"' \
742+ ' local shadow_hash' \
743+ ' shadow_hash=" $(getent shadow "${username}" | cut -d: -f2)"' \
744+ ' if [ -z " ${shadow_hash}" ]; then' \
745+ ' log " Failed to read shadow hash for ${username}"' \
746+ ' return 1' \
747+ ' fi' \
748+ ' local status locked' \
749+ ' status=" $(passwd -S "${username}" | tr -s " " | cut -d" " -f2)"' \
750+ ' locked=" false"' \
751+ ' if [ " ${status}" = " L" ]; then' \
752+ ' locked=" true"' \
753+ ' fi' \
754+ ' local record_path=" /var/lib/opencode-users/${username}.json"' \
755+ ' umask 077' \
756+ ' jq -n --arg username " ${username}" --arg hash " ${shadow_hash}" --argjson locked " ${locked}" '" '"' {username:$username,password_hash:$hash,locked:$locked}'"' "' > " ${record_path}"' \
757+ ' chmod 600 " ${record_path}"' \
758+ ' log " Persisted user record: ${username}"' \
759+ ' }' \
760+ '' \
761+ ' bootstrap_user() {' \
762+ ' local username=" ${OPENCODE_BOOTSTRAP_USER:-}"' \
763+ ' local password=" ${OPENCODE_BOOTSTRAP_PASSWORD:-}"' \
764+ ' local password_hash=" ${OPENCODE_BOOTSTRAP_PASSWORD_HASH:-}"' \
765+ ' if [ -z " ${username}" ]; then' \
766+ ' return 1' \
767+ ' fi' \
768+ ' if [ -z " ${password_hash}" ] && [ -z " ${password}" ]; then' \
769+ ' log " OPENCODE_BOOTSTRAP_USER is set but no password or hash provided"' \
770+ ' exit 1' \
771+ ' fi' \
772+ ' if ! id -u " ${username}" >/dev/null 2>&1; then' \
773+ ' log " Creating bootstrap user: ${username}"' \
774+ ' useradd -m -s /bin/bash " ${username}"' \
775+ ' fi' \
776+ ' if [ -n " ${password_hash}" ]; then' \
777+ ' usermod -p " ${password_hash}" " ${username}"' \
778+ ' else' \
779+ ' echo " ${username}:${password}" | chpasswd' \
780+ ' fi' \
781+ ' persist_user_record " ${username}"' \
782+ ' log " Bootstrap user ready: ${username}"' \
783+ ' return 0' \
784+ ' }' \
785+ '' \
786+ ' if restore_users; then' \
787+ ' log " User records restored"' \
788+ ' else' \
789+ ' if ! bootstrap_user; then' \
790+ ' log " No persisted users and no bootstrap user configured"' \
791+ ' fi' \
792+ ' fi' \
793+ '' \
794+ ' log " Starting opencode on ${OPENCODE_HOST}:${OPENCODE_PORT}"' \
694795 ' /usr/local/bin/opencode-broker &' \
695796 ' # Use runuser to switch to opencode user without password prompt' \
696- ' exec runuser -u opencode -- sh -lc " cd /home/opencode/workspace && /opt/opencode/bin/opencode web --port 3000 --hostname 0.0.0.0 "' \
797+ ' exec runuser -u opencode -- sh -lc " cd /home/opencode/workspace && /opt/opencode/bin/opencode web --port ${OPENCODE_PORT} --hostname ${OPENCODE_HOST} "' \
697798 'fi' \
698799 > /usr/local/bin/entrypoint.sh && chmod +x /usr/local/bin/entrypoint.sh
699800
@@ -706,7 +807,7 @@ RUN printf '%s\n' \
706807# Check that opencode main page responds
707808# Works for both tini and systemd modes
708809HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
709- CMD curl -f -H " Accept: text/html" http://localhost:3000/ || exit 1
810+ CMD [" sh ", " -c ", " OPENCODE_PORT= \" ${OPENCODE_PORT:-${PORT:-3000}} \" ; curl -f -H \ " Accept: text/html\" \" http://localhost:${OPENCODE_PORT}/ \" || exit 1"]
710811
711812# -----------------------------------------------------------------------------
712813# opencode Artifacts
0 commit comments