Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
53d60fd
Update README.md
jotka Dec 28, 2025
497fbdb
Update README.md
jotka Dec 29, 2025
e536388
Update README.md
jotka Dec 29, 2025
ab8743b
proper src structure, dockerfile, entrypoint
jotka Dec 29, 2025
f4a57ec
proper src structure, dockerfile, entrypoint
jotka Dec 29, 2025
ba05d16
cleanup .DS_Store
jotka Dec 29, 2025
9db6e67
drizzle config
jotka Dec 29, 2025
522154c
ignore
jotka Dec 29, 2025
81fcc28
Update LICENSE.txt
jotka Dec 29, 2025
53ca99a
Update README.md
jotka Dec 29, 2025
fcb36c4
bmac
jotka Dec 29, 2025
695acd9
bmac
jotka Dec 29, 2025
c60db29
compose example
jotka Dec 29, 2025
cd6544a
1.0.5
jotka Jan 1, 2026
de62327
1.0.5
jotka Jan 1, 2026
215f52b
1.0.5
jotka Jan 1, 2026
6a7116a
1.0.5
jotka Jan 2, 2026
3eab421
Update README.md
jotka Jan 2, 2026
059ecbb
Update README.md
jotka Jan 2, 2026
a8a5623
1.0.5
jotka Jan 2, 2026
c46870a
1.0.5
jotka Jan 2, 2026
86e4c9e
1.0.5
jotka Jan 3, 2026
a02115e
missing scripts
jotka Jan 3, 2026
410d542
1.0.6
jotka Jan 3, 2026
b269b8d
1.0.7
jotka Jan 11, 2026
6382b40
1.0.7
jotka Jan 11, 2026
6baf6c2
1.0.7
jotka Jan 11, 2026
f588ed7
Improve Environment Layout on Mobile
sieren Jan 8, 2026
f972378
Mobile: Only show total of stacks
sieren Jan 8, 2026
107e9c3
1.0.8
jotka Jan 14, 2026
e8ab07e
1.0.9
jotka Jan 17, 2026
6d9b509
1.0.10
jotka Jan 18, 2026
fd744ed
Update package.json
jotka Jan 19, 2026
80a5bbd
Update README.md
jotka Jan 19, 2026
6cb948e
Update README.md
jotka Jan 19, 2026
261d940
Update README.md
jotka Jan 19, 2026
566d800
Create ai-opt-out
jotka Jan 19, 2026
750c9c1
feat: add SYS_RAWIO to container capabilities list
FlintyLemming Jan 14, 2026
7f9862f
1.0.11
jotka Jan 20, 2026
0303f54
1.0.12
jotka Jan 22, 2026
dd6c5fd
1.0.12
jotka Jan 22, 2026
fd35a0a
1.0.12
jotka Jan 22, 2026
1a95f5a
Honor DATA_DIR env var in sqlite operations related to hawser connect…
vikte Jan 23, 2026
1036cd0
#96
jotka Jan 23, 2026
193dc44
Update README.md
jotka Jan 24, 2026
7d6f6f2
Update README.md
jotka Jan 24, 2026
2364755
1.0.13
jotka Jan 28, 2026
53be8f8
1.0.14
jotka Jan 31, 2026
ced84b5
Add option to pull image before container update
shamoon Feb 3, 2026
70e2166
Only show on update
shamoon Feb 3, 2026
13c9784
Add unit test coverage for core modules
TimElschner Feb 5, 2026
4164a21
Fix api-smoke test: correct system endpoint path
TimElschner Feb 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
buy_me_a_coffee:
displayName: "Buy Me a Coffee"
account: dockhand
1 change: 1 addition & 0 deletions .github/ai-opt-out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
opt-out: true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea/
.DS_Store
186 changes: 186 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# syntax=docker/dockerfile:1.4
# =============================================================================
# Dockhand Docker Image - Security-Hardened Build
# =============================================================================
# This Dockerfile builds a custom Wolfi OS from scratch using apko, ensuring:
# - Full transparency (no dependency on pre-built Chainguard images)
# - Reproducible builds from open-source Wolfi packages
# - Minimal attack surface with only required packages
#
# Bun is copied from the official oven/bun image (app-builder stage).
# For CPUs without AVX support (Celeron, Atom, pre-Haswell), build with:
# docker build --build-arg BUN_VARIANT=baseline -t dockhand:baseline .
# =============================================================================

# -----------------------------------------------------------------------------
# Stage 1: OS Generator (Alpine + apko tool)
# -----------------------------------------------------------------------------
# We use Alpine because it has a shell. This lets us download and run apko
# to build our custom Wolfi OS from scratch using open-source packages.
FROM alpine:3.21 AS os-builder

ARG TARGETARCH

WORKDIR /work

# Install apko tool (latest stable release)
# apko is the tool Chainguard uses to build their images - we use it directly
ARG APKO_VERSION=0.30.34
RUN apk add --no-cache curl unzip \
&& ARCH=$([ "$TARGETARCH" = "arm64" ] && echo "arm64" || echo "amd64") \
&& curl -sL "https://github.com/chainguard-dev/apko/releases/download/v${APKO_VERSION}/apko_${APKO_VERSION}_linux_${ARCH}.tar.gz" \
| tar -xz --strip-components=1 -C /usr/local/bin \
&& chmod +x /usr/local/bin/apko

# Generate apko.yaml for current target architecture only
# We build single-arch to avoid multi-arch layer confusion in extraction
# Note: Bun is NOT included here - it's copied from app-builder stage for CPU compatibility
RUN APKO_ARCH=$([ "$TARGETARCH" = "arm64" ] && echo "aarch64" || echo "x86_64") \
&& printf '%s\n' \
"contents:" \
" repositories:" \
" - https://packages.wolfi.dev/os" \
" keyring:" \
" - https://packages.wolfi.dev/os/wolfi-signing.rsa.pub" \
" packages:" \
" - wolfi-base" \
" - ca-certificates" \
" - busybox" \
" - tzdata" \
" - docker-cli" \
" - docker-compose" \
" - docker-cli-buildx" \
" - sqlite" \
" - postgresql-client" \
" - git" \
" - openssh-client" \
" - curl" \
" - tini" \
" - su-exec" \
"entrypoint:" \
" command: /bin/sh -l" \
"archs:" \
" - ${APKO_ARCH}" \
> apko.yaml

# Build the OS tarball and extract rootfs
# apko creates an OCI tarball - we need to extract the actual filesystem layer
RUN apko build apko.yaml dockhand-base:latest output.tar \
&& mkdir -p rootfs \
&& tar -xf output.tar \
&& LAYER=$(tar -tf output.tar | grep '.tar.gz$' | head -1) \
&& tar -xzf "$LAYER" -C rootfs

# -----------------------------------------------------------------------------
# Stage 2: Application Builder
# -----------------------------------------------------------------------------
# Using Debian to avoid Alpine musl thread creation issues
# Alpine's musl libc causes rayon/tokio thread pool panics during svelte-adapter-bun build
FROM oven/bun:1.3.5-debian AS app-builder

# Build argument for Bun variant (regular or baseline)
# baseline is for CPUs without AVX support (Celeron, Atom, pre-Haswell)
ARG BUN_VARIANT=regular
ARG TARGETARCH

WORKDIR /app

# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends jq git curl unzip ca-certificates && rm -rf /var/lib/apt/lists/*

# Copy package files and install ALL dependencies (needed for build)
COPY package.json bun.lock* bunfig.toml ./
RUN bun install --frozen-lockfile

# Copy source code and build
COPY . .

# Build with parallelism - dedicated build VM has 16 CPUs and 32GB RAM
RUN NODE_OPTIONS="--max-old-space-size=8192 --max-semi-space-size=128" bun run build

# Prepare production node_modules (do this in builder where we have compilers)
# This ensures native addons compile correctly before copying to hardened runtime
RUN rm -rf node_modules && bun install --production --frozen-lockfile \
&& rm -rf node_modules/@types node_modules/bun-types

# Download baseline Bun binary if BUN_VARIANT=baseline (for CPUs without AVX)
# Only applies to amd64 - ARM64 doesn't have AVX concept
ARG BUN_VERSION=1.3.5
RUN if [ "$BUN_VARIANT" = "baseline" ] && [ "$TARGETARCH" = "amd64" ]; then \
echo "Downloading Bun baseline binary for CPUs without AVX support..." && \
curl -fsSL "https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/bun-linux-x64-baseline.zip" -o /tmp/bun.zip && \
unzip -o /tmp/bun.zip -d /tmp && \
cp /tmp/bun-linux-x64-baseline/bun /usr/local/bin/bun && \
chmod +x /usr/local/bin/bun && \
rm -rf /tmp/bun.zip /tmp/bun-linux-x64-baseline && \
echo "Bun baseline binary installed successfully"; \
fi

# -----------------------------------------------------------------------------
# Stage 3: Final Image (Scratch + Custom Wolfi OS)
# -----------------------------------------------------------------------------
FROM scratch

# Install our custom-built Wolfi OS (now we have /bin/sh!)
COPY --from=os-builder /work/rootfs/ /

# Copy Bun from official image - ensures compatibility with all x86_64 CPUs (no AVX2 requirement)
# Wolfi's bun package requires AVX2 which breaks on Celeron/Atom CPUs
# For baseline builds (BUN_VARIANT=baseline), this contains the baseline binary (no AVX requirement)
# For regular builds, this contains the standard oven/bun binary
COPY --from=app-builder /usr/local/bin/bun /usr/bin/bun

WORKDIR /app

# Set up environment variables
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \
NODE_ENV=production \
PORT=3000 \
HOST=0.0.0.0 \
DATA_DIR=/app/data \
HOME=/home/dockhand \
PUID=1001 \
PGID=1001

# Create docker compose plugin symlink (we use `docker compose` syntax, Wolfi has standalone binary)
# Note: docker-cli-buildx package already creates the buildx symlink
RUN mkdir -p /usr/libexec/docker/cli-plugins \
&& ln -s /usr/bin/docker-compose /usr/libexec/docker/cli-plugins/docker-compose

# Create dockhand user and group (using busybox commands)
RUN addgroup -g 1001 dockhand \
&& adduser -u 1001 -G dockhand -h /home/dockhand -D dockhand

# Copy application files with correct ownership (avoids layer duplication from chown -R)
COPY --from=app-builder --chown=dockhand:dockhand /app/node_modules ./node_modules
COPY --from=app-builder --chown=dockhand:dockhand /app/package.json ./
COPY --from=app-builder --chown=dockhand:dockhand /app/build ./build
COPY --from=app-builder --chown=dockhand:dockhand /app/build/subprocesses/ ./subprocesses/

# Copy database migrations
COPY --chown=dockhand:dockhand drizzle/ ./drizzle/
COPY --chown=dockhand:dockhand drizzle-pg/ ./drizzle-pg/

# Copy legal documents
COPY --chown=dockhand:dockhand LICENSE.txt PRIVACY.txt ./

# Copy entrypoint script (root-owned, executable)
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

# Copy emergency scripts
COPY --chown=dockhand:dockhand scripts/emergency/ ./scripts/
RUN chmod +x ./scripts/*.sh ./scripts/**/*.sh 2>/dev/null || true

# Create data directories with correct ownership
RUN mkdir -p /home/dockhand/.dockhand/stacks /app/data \
&& chown dockhand:dockhand /app/data /home/dockhand /home/dockhand/.dockhand /home/dockhand/.dockhand/stacks

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/ || exit 1

ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"]
CMD ["bun", "run", "./build/index.js"]
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,6 @@ under an Open Source License, as stated in this License.

For licensing inquiries, commercial licensing, or enterprise features:

Website: https://dockhand.io
Website: https://dockhand.pro

-----------------------------------------------------------------------------
Loading