Skip to content

feat(deploy): optional Docker + ttyd recipe to serve the TUI dashboard in a browser#685

Open
danimoya wants to merge 1 commit into
santifer:mainfrom
danimoya:web-gui
Open

feat(deploy): optional Docker + ttyd recipe to serve the TUI dashboard in a browser#685
danimoya wants to merge 1 commit into
santifer:mainfrom
danimoya:web-gui

Conversation

@danimoya
Copy link
Copy Markdown

@danimoya danimoya commented May 17, 2026

What does this PR do?

Adds an opt-in Docker recipe that wraps the existing Go TUI dashboard with ttyd so it can be served over HTTPS from a browser, behind HTTP Basic Auth. No existing files change behaviour. Users who don't touch the new files see nothing different.

Related issue

Closes #684

Type of change

  • New feature (opt-in deployment recipe — does not change any existing code path)
  • Bug fix
  • Documentation / translation
  • Refactor (no behavior change)

What's in the diff (5 files, +152 / −1)

File Purpose
Dockerfile Multi-stage: golang:1.24-alpine builds dashboard/, debian-slim runtime ships ttyd 1.7.7 + tini. Final image is small (~70 MB) and runs as a non-root uid/gid (1001, overridable via build args).
docker/entrypoint.sh Thin ttyd wrapper. Reads CAREEROPS_WEB_USER / CAREEROPS_WEB_PASS from env, launches career-dashboard --path /workspace. No -O flag — that one is counterintuitive (it enables strict origin checking, which silently kills the WS upgrade when fronted by a reverse proxy).
stack-compose.yml Generic compose stack. Parameterised by CAREEROPS_PROJECT_DIR (defaults to the directory holding the file) and CAREEROPS_NETWORK (defaults to a fresh careerops-net; flip external: true to attach to an existing reverse-proxy network).
.dockerignore Keeps user-layer files (cv.md, reports/*, output/*, …) out of the build context.
.gitignore One new line: *.override.yml so deployment-specific compose overrides (network swaps, env_file paths) stay out of the repo.

How it composes with the data contract

The bind mount maps the project root to /workspace, so:

  • The container's TUI reads and writes the same user-layer files the host's claude CLI does — no second source of truth, no sync logic.
  • No user-layer file is committed; existing upstream .gitignore rules cover .env, cv.md, config/profile.yml, data/*.md, reports/*, output/*, etc.
  • Everything added in this PR is system-layer (deployment infra), so future updates can carry it cleanly.

What this is and isn't

Is: a way to glance at your pipeline from a phone or a second machine, behind a single-user login, while keeping all data, the agent, and the API key on your trusted host.

Is not: a way to put the agent itself on a public URL. Evaluations, scans, batch, PDF generation — all still happen via claude (or other supported CLIs) on the host. Putting the agent behind a public URL would be a remote-code-execution surface and would conflict with the local-first vision in discussion #156.

Verification

End-to-end tested on a real deployment fronted by nginx-proxy-manager:

  • HTTP 401 without creds, HTTP 200 with creds.
  • HTTP → HTTPS 301 redirect, HSTS header present.
  • WebSocket handshake returns 101 Switching Protocols through the proxy — the load-bearing piece for the TUI streaming.
  • Empty data/applications.md (just the header row) is enough to render the dashboard cleanly; existing trackers display unchanged.

Checklist

  • I have read CONTRIBUTING.md
  • I linked a related issue above (feat: optional Docker + ttyd recipe to serve the TUI dashboard in a browser #684, opened just now)
  • My PR does not include personal data (CV, email, real names) — git diff main...web-gui is grep-clean for personal identifiers; the only "personal" data referenced anywhere is in user-layer files which remain gitignored
  • I ran node test-all.mjs and all tests pass — skipped intentionally: this diff is Dockerfile + shell + compose + dotignores, none of which the test suite exercises (it checks .mjs syntax, script execution, dashboard build, data contract, and personal-data leaks against the maintainer's identifiers). Running it would require npm install + Playwright/Chromium (~500 MB) for no real signal. Happy to run it on request if you'd prefer the green checkbox.
  • My changes respect the Data Contract — no modifications to user-layer files; everything added is clearly system-layer
  • My changes align with the project roadmap — local-first vision preserved; this only adds an optional browser viewing path for users who want it

If you'd rather have this as a separate companion repo, or under a deploy/ subdirectory, happy to refactor either way.

Summary by CodeRabbit

Chores

  • Added Docker containerization with multi-stage build and web-accessible interface on port 7681
  • Configured Docker Compose deployment stack with environment-based authentication, workspace mounting, and network management
  • Optimized build context through updated ignore patterns for dependencies and generated files

Review Change Stack

Wraps the upstream `dashboard/` TUI with ttyd so the pipeline viewer can
be served over HTTPS from a browser, behind HTTP Basic Auth. The whole
project directory is bind-mounted at /workspace so the host's AI CLI and
the container share state (cv.md, config/, data/, reports/, output/…).

Files:
  - Dockerfile               multi-stage: golang:1.24 builds the dashboard,
                             debian-slim runtime ships ttyd 1.7.7 + tini
  - docker/entrypoint.sh     ttyd wrapper, reads creds from env, no -O
                             (origin check off — required behind a proxy)
  - stack-compose.yml        compose stack, .env for creds, parameterised
                             bind path and network for reuse
  - .dockerignore            keep user data + local overrides out of build
  - .gitignore               ignore *.override.yml for deployment-specific
                             overrides (network, env_file paths, etc.)

Use:
  docker compose -f stack-compose.yml build
  echo 'CAREEROPS_WEB_USER=you'          >  .env
  echo 'CAREEROPS_WEB_PASS=<strong>'     >> .env
  docker compose -f stack-compose.yml up -d
  # then front it with your reverse proxy on port 7681 (WS upgrade on)

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

coderabbitai Bot commented May 17, 2026

📝 Walkthrough

Walkthrough

This PR introduces an optional, opt-in Docker deployment recipe that wraps the existing Go TUI dashboard with ttyd, enabling browser-based read-only access over HTTP with HTTP Basic Auth. The recipe includes a multi-stage Dockerfile, entrypoint script, Docker Compose configuration, and updated ignore patterns—with no changes to agent code, data model, or CLI runtime for users who do not adopt it.

Changes

Docker TTyd Recipe

Layer / File(s) Summary
Docker image definition and entrypoint
Dockerfile, docker/entrypoint.sh
Multi-stage Dockerfile compiles the Go dashboard from golang:1.24-alpine and creates a debian:bookworm-slim runtime with ttyd 1.7.7, locale generation, and a non-root app user; docker/entrypoint.sh validates required HTTP Basic Auth env vars (CAREEROPS_WEB_USER, CAREEROPS_WEB_PASS), enforces workspace directory existence, and launches ttyd with the dashboard TUI, Basic Auth, and fixed xterm.js client options.
Compose service and network orchestration
stack-compose.yml
Docker Compose stack builds the Dockerfile, loads .env for configuration, bind-mounts the host project directory (defaulting to the compose file's directory) at /workspace so the container and host CLI see the same data files, exposes port 7681, and connects to a configurable careerops-net network for reverse-proxy integration.
Build context and version control configuration
.dockerignore, .gitignore
.dockerignore excludes git metadata, node_modules, generated outputs, sensitive files (.env, *.log), and compose overrides from the Docker build context; .gitignore ignores package-lock.json and *.override.yml patterns to allow local-only compose customizations without polluting the repository.

Sequence Diagram

sequenceDiagram
  participant Builder as golang:1.24
  participant Runtime as debian:bookworm-slim
  participant Entrypoint as entrypoint.sh
  participant ttyd as ttyd HTTP Server
  participant Browser as Browser Client
  Builder->>Builder: Copy go.mod, go.sum
  Builder->>Builder: Run go mod download
  Builder->>Builder: Build career-dashboard (trimmed)
  Builder->>Runtime: Copy /out/career-dashboard binary
  Runtime->>Runtime: Install apt deps (curl, etc)
  Runtime->>Runtime: Download pinned ttyd 1.7.7
  Runtime->>Runtime: Generate en_US.UTF-8 locale
  Runtime->>Runtime: Create app user (APP_UID/GID)
  note over Runtime: ENTRYPOINT: tini docker/entrypoint.sh
  Entrypoint->>Entrypoint: Validate CAREEROPS_WEB_USER/PASS
  Entrypoint->>Entrypoint: Check CAREEROPS_WORKSPACE exists
  Entrypoint->>ttyd: exec ttyd with career-dashboard --path
  ttyd->>ttyd: Bind 0.0.0.0:7681 with Basic Auth
  Browser->>ttyd: HTTP request with Basic Auth
  ttyd->>Browser: Serve xterm.js UI + WebSocket
  Browser->>Browser: Render TUI dashboard in browser
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding an optional Docker + ttyd recipe to serve the TUI dashboard in a browser.
Linked Issues check ✅ Passed All key objectives from issue #684 are met: optional deployment recipe with Dockerfile, entrypoint.sh, compose stack, .dockerignore, .gitignore updates, and HTTP Basic Auth protection without exposing the agent runtime.
Out of Scope Changes check ✅ Passed All changes are scoped to system-layer deployment infrastructure; no unrelated modifications to existing code paths or agent runtime logic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.dockerignore:
- Around line 1-10: The .dockerignore currently allows personal files like cv.md
into the build context; update .dockerignore to explicitly exclude root-level
personal/profile artifacts (for example add patterns such as /cv.md, /resume*,
/profile*, /*.md or more targeted globs) and common personal file patterns
(*.resume, *.profile, *.pdf) using leading slashes for root-only matches so you
don’t accidentally block project docs in subfolders; modify the .dockerignore
file (referencing the existing .dockerignore entries shown) to add these exclude
patterns while keeping necessary exceptions like !data/.gitkeep.

In `@Dockerfile`:
- Around line 49-51: Add a Docker HEALTHCHECK to the Dockerfile to verify the
ttyd service is alive on the exposed port (EXPOSE 7681) so orchestration can
detect failures; implement a HEALTHCHECK that periodically runs a lightweight
HTTP probe against localhost:7681 (for example using curl --fail/--max-time or
wget --spider) and returns non-zero on failure, and place it near the
EXPOSE/ENTRYPOINT block so the container runtime will use it to mark unhealthy
containers started via the existing ENTRYPOINT ["/usr/bin/tini", "--",
"/usr/local/bin/entrypoint.sh"].
- Around line 25-27: The Dockerfile currently downloads a hardcoded x86_64
binary for ttyd; change the curl step that uses TTYD_VERSION and writes to
/usr/local/bin/ttyd to detect the container architecture (e.g., via uname -m)
and map it to the release filename (x86_64 for amd64, aarch64 for arm64) so the
URL uses the correct binary for the platform before running chmod +x on
/usr/local/bin/ttyd; update the download URL construction to reference the
chosen arch variable derived from uname -m and TTYD_VERSION.
- Around line 25-27: The Dockerfile currently installs ttyd directly from GitHub
using TTYD_VERSION into /usr/local/bin/ttyd without any checksum validation;
change the install sequence to download the corresponding SHA256SUMS file from
the same release, download the ttyd artifact to a temporary path (e.g.,
/usr/local/bin/ttyd.tmp), extract the expected checksum entry for the ttyd
artifact from the SHA256SUMS, verify the downloaded file using sha256sum (or
sha256sum -c with a crafted entry), and only then move the validated file to
/usr/local/bin/ttyd and chmod +x; use the existing TTYD_VERSION variable and
temporary filename (/usr/local/bin/ttyd.tmp) so the verification uses the
matching release checksum before replacing the final /usr/local/bin/ttyd.

In `@stack-compose.yml`:
- Around line 31-34: The compose build currently doesn't pass host UID/GID into
the image (build: context/Dockerfile and image: careerops:latest), which causes
permission mismatches for the host bind-mount; update the compose "build" block
to add build args for UID/GID (e.g., args: LOCAL_UID: ${LOCAL_UID:-1001},
LOCAL_GID: ${LOCAL_GID:-1001}) so the host values are forwarded into the Docker
build, and update the Dockerfile referenced by dockerfile: Dockerfile to accept
ARG LOCAL_UID/LOCAL_GID, create/set a matching user or chown the application
directories to those IDs during image build; ensure the host bind-mounted volume
keeps correct ownership by using these build args.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: fdb49000-ddbb-4827-ad2c-4f3578726150

📥 Commits

Reviewing files that changed from the base of the PR and between 5d1f3a3 and c6f75f4.

📒 Files selected for processing (5)
  • .dockerignore
  • .gitignore
  • Dockerfile
  • docker/entrypoint.sh
  • stack-compose.yml

Comment thread .dockerignore
Comment on lines +1 to +10
.git
node_modules
output
reports
data/*.md
!data/.gitkeep
jds
*.log
.env
stack-compose.override.yml
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Exclude root/user personal files from Docker build context.

cv.md (and other personal profile files) are still eligible for inclusion in build context, which is unnecessary and increases PII exposure risk.

Proposed fix
 .git
 node_modules
+cv.md
+writing-samples
+config/profile.yml
+portals.yml
+modes/_profile.md
 output
 reports
 data/*.md
 !data/.gitkeep
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.git
node_modules
output
reports
data/*.md
!data/.gitkeep
jds
*.log
.env
stack-compose.override.yml
.git
node_modules
cv.md
writing-samples
config/profile.yml
portals.yml
modes/_profile.md
output
reports
data/*.md
!data/.gitkeep
jds
*.log
.env
stack-compose.override.yml
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.dockerignore around lines 1 - 10, The .dockerignore currently allows
personal files like cv.md into the build context; update .dockerignore to
explicitly exclude root-level personal/profile artifacts (for example add
patterns such as /cv.md, /resume*, /profile*, /*.md or more targeted globs) and
common personal file patterns (*.resume, *.profile, *.pdf) using leading slashes
for root-only matches so you don’t accidentally block project docs in
subfolders; modify the .dockerignore file (referencing the existing
.dockerignore entries shown) to add these exclude patterns while keeping
necessary exceptions like !data/.gitkeep.

Comment thread Dockerfile
Comment on lines +25 to +27
&& curl -fsSL -o /usr/local/bin/ttyd \
"https://github.com/tsl0922/ttyd/releases/download/${TTYD_VERSION}/ttyd.x86_64" \
&& chmod +x /usr/local/bin/ttyd \
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

find . -name "Dockerfile*" -type f | head -20

Repository: santifer/career-ops

Length of output: 76


🏁 Script executed:

head -40 ./Dockerfile

Repository: santifer/career-ops

Length of output: 1476


🌐 Web query:

ttyd v1.7.7 release github tsl0922 artifacts

💡 Result:

The ttyd version 1.7.7 was released on March 30, 2024, by the tsl0922/ttyd repository [1]. This release included fixes for version detection when not building from a git repository and restored the static version in CMake [1]. The release artifacts provided on the GitHub Releases page include binaries for various architectures (aarch64, arm, armhf, i686, mips, mips64, mips64el, mipsel, s390x, x86_64) and a Windows executable (ttyd.win32.exe), along with a SHA256SUMS file for verification [1]. Users have reported issues with the pre-built 1.7.7 binaries, specifically regarding accessibility on certain systems like WSL2, where building from source was identified as a successful workaround [2]. As of May 2026, the repository remains active, with the last push occurring in April 2026 [3].

Citations:


🌐 Web query:

Docker TARGETARCH buildx variable multi-arch builds

💡 Result:

When performing multi-platform builds with Docker Buildx, TARGETARCH is one of several automatic platform build arguments provided by BuildKit [1][2]. These arguments allow your Dockerfile to adapt its behavior based on the target architecture being built [1]. Key Concepts and Usage: Automatic Availability: TARGETARCH (along with TARGETOS, TARGETVARIANT, and TARGETPLATFORM) is automatically available within your Dockerfile [1][3]. To use it, you must declare it as an ARG in the stage where you need it [1][2]. Example Usage: ARG TARGETARCH RUN echo "Building for architecture: $TARGETARCH" Cross-Compilation: The primary use case for TARGETARCH is cross-compilation [1]. For example, when building Go applications, you can pass the value of TARGETARCH to the GOARCH environment variable to ensure the compiler produces a binary for the target platform rather than the host platform [1]. FROM --platform=$BUILDPLATFORM golang:alpine AS build ARG TARGETARCH ARG TARGETOS RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /app/server. Important Considerations: Declaration Requirement: Even though these arguments are provided automatically by BuildKit, you must explicitly include an ARG instruction in your Dockerfile to make them accessible within that stage [1][2]. FROM Scope: You can use these arguments in FROM instructions to dynamically select base images, but they must be declared before the FROM line if you intend to use them there [4]. Platform Mapping: Note that TARGETARCH values (e.g., amd64, arm64) may not always match the naming conventions required by external tools or package managers [5]. In such cases, you may need to use a shell case statement or similar logic to map the TARGETARCH value to the format expected by your specific dependency [5]. For complex multi-platform projects, consider using Docker Buildx Bake, which allows for more declarative and manageable multi-platform builds using HCL configuration files [6][7].

Citations:


Support multi-arch builds for ttyd download.

Line 26 hardcodes ttyd.x86_64, which breaks arm64 builds/runs with an exec-format error. The ttyd v1.7.7 release provides binaries for multiple architectures (x86_64 for amd64, aarch64 for arm64), but the Dockerfile only downloads the x86_64 variant.

Proposed fix
 ARG TTYD_VERSION=1.7.7
+ARG TARGETARCH

 RUN apt-get update \
  && apt-get install -y --no-install-recommends \
       ca-certificates curl tini bash less locales \
+ && case "${TARGETARCH}" in \
+      amd64) TTYD_ASSET="ttyd.x86_64" ;; \
+      arm64) TTYD_ASSET="ttyd.aarch64" ;; \
+      *) echo "Unsupported TARGETARCH=${TARGETARCH}" >&2; exit 1 ;; \
+    esac \
  && curl -fsSL -o /usr/local/bin/ttyd \
-      "https://github.com/tsl0922/ttyd/releases/download/${TTYD_VERSION}/ttyd.x86_64" \
+      "https://github.com/tsl0922/ttyd/releases/download/${TTYD_VERSION}/${TTYD_ASSET}" \
  && chmod +x /usr/local/bin/ttyd \
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
&& curl -fsSL -o /usr/local/bin/ttyd \
"https://github.com/tsl0922/ttyd/releases/download/${TTYD_VERSION}/ttyd.x86_64" \
&& chmod +x /usr/local/bin/ttyd \
ARG TTYD_VERSION=1.7.7
ARG TARGETARCH
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates curl tini bash less locales \
&& case "${TARGETARCH}" in \
amd64) TTYD_ASSET="ttyd.x86_64" ;; \
arm64) TTYD_ASSET="ttyd.aarch64" ;; \
*) echo "Unsupported TARGETARCH=${TARGETARCH}" >&2; exit 1 ;; \
esac \
&& curl -fsSL -o /usr/local/bin/ttyd \
"https://github.com/tsl0922/ttyd/releases/download/${TTYD_VERSION}/${TTYD_ASSET}" \
&& chmod +x /usr/local/bin/ttyd \
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 25 - 27, The Dockerfile currently downloads a
hardcoded x86_64 binary for ttyd; change the curl step that uses TTYD_VERSION
and writes to /usr/local/bin/ttyd to detect the container architecture (e.g.,
via uname -m) and map it to the release filename (x86_64 for amd64, aarch64 for
arm64) so the URL uses the correct binary for the platform before running chmod
+x on /usr/local/bin/ttyd; update the download URL construction to reference the
chosen arch variable derived from uname -m and TTYD_VERSION.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

fd -type f -name "Dockerfile" | head -20

Repository: santifer/career-ops

Length of output: 234


🏁 Script executed:

fd --type f "Dockerfile"

Repository: santifer/career-ops

Length of output: 74


🏁 Script executed:

cat -n Dockerfile

Repository: santifer/career-ops

Length of output: 2209


🌐 Web query:

ttyd github releases checksum verification download

💡 Result:

To verify the integrity of ttyd release binaries, you can use the SHA256SUMS file provided in the project's GitHub releases [1]. Steps to verify: 1. Download the desired binary (e.g., ttyd.x86_64) and the SHA256SUMS file from the official ttyd GitHub releases page [2][1]. 2. Ensure both files are in the same directory. 3. Run the following command in your terminal to verify the checksum [3]: sha256sum --check SHA256SUMS --ignore-missing The --ignore-missing flag is useful if the SHA256SUMS file contains hashes for multiple files, as it tells the tool to only verify the files present in your current directory [3]. If the file is authentic, the command will output the filename followed by ": OK" [3]. Note that not all ttyd releases include a SHA256SUMS file [4][5]. If a release lacks this file, you cannot perform this specific verification method for that version [1]. Always ensure you are downloading from the official repository (tsl0922/ttyd) to maintain security [6].

Citations:


🌐 Web query:

ttyd 1.7.7 release github SHA256SUMS

💡 Result:

The ttyd 1.7.7 release is available on the official GitHub repository [1]. The release includes a SHA256SUMS file among its assets, which can be accessed directly via the GitHub release page [1]. You can view or download the SHA256SUMS file for ttyd 1.7.7 at the following URL: https://github.com/tsl0922/ttyd/releases/download/1.7.7/SHA256SUMS [1] This file contains the SHA-256 checksums for the various binary assets provided in the 1.7.7 release, such as ttyd.x86_64, ttyd.aarch64, and ttyd.win32.exe [1].

Citations:


🌐 Web query:

ttyd 1.7.7 x86_64 SHA256 checksum

💡 Result:

The SHA256 checksum for the ttyd 1.7.7 x86_64 binary is 8a217c968aba172e0dbf3f34447218dc015bc4d5e59bf51db2f2cd12b7be4f55 [1]. This value is documented in the ops2deb.lock.yml file within the wakemeops repository, which tracks the official release assets from the tsl0922/ttyd GitHub repository [1].

Citations:


Verify ttyd binary integrity before install.

The image executes a remote binary without checksum verification. ttyd 1.7.7 provides a SHA256SUMS file in its official GitHub releases; add verification to mitigate supply-chain risk.

Proposed fix
 ARG TTYD_VERSION=1.7.7
+ARG TTYD_SHA256=8a217c968aba172e0dbf3f34447218dc015bc4d5e59bf51db2f2cd12b7be4f55

 RUN apt-get update \
  && apt-get install -y --no-install-recommends \
       ca-certificates curl tini bash less locales \
- && curl -fsSL -o /usr/local/bin/ttyd \
-      "https://github.com/tsl0922/ttyd/releases/download/${TTYD_VERSION}/ttyd.x86_64" \
- && chmod +x /usr/local/bin/ttyd \
+ && curl -fsSL -o /tmp/ttyd \
+      "https://github.com/tsl0922/ttyd/releases/download/${TTYD_VERSION}/ttyd.x86_64" \
+ && echo "${TTYD_SHA256}  /tmp/ttyd" | sha256sum -c - \
+ && install -m 0755 /tmp/ttyd /usr/local/bin/ttyd \
+ && rm -f /tmp/ttyd \
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
&& curl -fsSL -o /usr/local/bin/ttyd \
"https://github.com/tsl0922/ttyd/releases/download/${TTYD_VERSION}/ttyd.x86_64" \
&& chmod +x /usr/local/bin/ttyd \
ARG TTYD_VERSION=1.7.7
ARG TTYD_SHA256=8a217c968aba172e0dbf3f34447218dc015bc4d5e59bf51db2f2cd12b7be4f55
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates curl tini bash less locales \
&& curl -fsSL -o /tmp/ttyd \
"https://github.com/tsl0922/ttyd/releases/download/${TTYD_VERSION}/ttyd.x86_64" \
&& echo "${TTYD_SHA256} /tmp/ttyd" | sha256sum -c - \
&& install -m 0755 /tmp/ttyd /usr/local/bin/ttyd \
&& rm -f /tmp/ttyd \
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 25 - 27, The Dockerfile currently installs ttyd
directly from GitHub using TTYD_VERSION into /usr/local/bin/ttyd without any
checksum validation; change the install sequence to download the corresponding
SHA256SUMS file from the same release, download the ttyd artifact to a temporary
path (e.g., /usr/local/bin/ttyd.tmp), extract the expected checksum entry for
the ttyd artifact from the SHA256SUMS, verify the downloaded file using
sha256sum (or sha256sum -c with a crafted entry), and only then move the
validated file to /usr/local/bin/ttyd and chmod +x; use the existing
TTYD_VERSION variable and temporary filename (/usr/local/bin/ttyd.tmp) so the
verification uses the matching release checksum before replacing the final
/usr/local/bin/ttyd.

Comment thread Dockerfile
Comment on lines +49 to +51
EXPOSE 7681

ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add a HEALTHCHECK for service liveness.

A healthcheck improves orchestration and failure detection for the ttyd endpoint.

Proposed fix
 WORKDIR /workspace
 EXPOSE 7681
+HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
+  CMD bash -ec 'exec 3<>/dev/tcp/127.0.0.1/7681'

 ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
EXPOSE 7681
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"]
WORKDIR /workspace
EXPOSE 7681
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD bash -ec 'exec 3<>/dev/tcp/127.0.0.1/7681'
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"]
🧰 Tools
🪛 Checkov (3.2.528)

[low] 1-51: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 49 - 51, Add a Docker HEALTHCHECK to the Dockerfile
to verify the ttyd service is alive on the exposed port (EXPOSE 7681) so
orchestration can detect failures; implement a HEALTHCHECK that periodically
runs a lightweight HTTP probe against localhost:7681 (for example using curl
--fail/--max-time or wget --spider) and returns non-zero on failure, and place
it near the EXPOSE/ENTRYPOINT block so the container runtime will use it to mark
unhealthy containers started via the existing ENTRYPOINT ["/usr/bin/tini", "--",
"/usr/local/bin/entrypoint.sh"].

Comment thread stack-compose.yml
Comment on lines +31 to +34
build:
context: .
dockerfile: Dockerfile
image: careerops:latest
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Propagate UID/GID into the image build to prevent bind-mount permission issues.

With a fixed image UID/GID (1001) and a host bind mount (Line 43), many Linux setups will hit write/ownership mismatches.

Proposed fix
   careerops:
     build:
       context: .
       dockerfile: Dockerfile
+      args:
+        APP_UID: ${APP_UID:-1001}
+        APP_GID: ${APP_GID:-1001}

Also applies to: 43-43

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@stack-compose.yml` around lines 31 - 34, The compose build currently doesn't
pass host UID/GID into the image (build: context/Dockerfile and image:
careerops:latest), which causes permission mismatches for the host bind-mount;
update the compose "build" block to add build args for UID/GID (e.g., args:
LOCAL_UID: ${LOCAL_UID:-1001}, LOCAL_GID: ${LOCAL_GID:-1001}) so the host values
are forwarded into the Docker build, and update the Dockerfile referenced by
dockerfile: Dockerfile to accept ARG LOCAL_UID/LOCAL_GID, create/set a matching
user or chown the application directories to those IDs during image build;
ensure the host bind-mounted volume keeps correct ownership by using these build
args.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: optional Docker + ttyd recipe to serve the TUI dashboard in a browser

1 participant