镜像构建历史
# 2026-08-30 13:23:49 0.00B 配置容器启动时运行的命令
ENTRYPOINT ["/app/entrypoint.sh"]
# 2026-08-30 13:23:49 0.00B 指定检查容器健康状态的命令
HEALTHCHECK {Test:[CMD-SHELL python /app/healthcheck.py] Interval:30s Timeout:10s StartPeriod:1m0s StartInterval:0s Retries:3}
# 2026-08-30 13:23:49 0.00B 声明容器运行时监听的端口
EXPOSE [3782/tcp 8001/tcp]
# 2026-08-30 13:23:49 358.00B 执行命令并创建新的镜像层
RUN /bin/sh -c cat > /app/healthcheck.py <<'EOF'
from pathlib import Path
import json
import urllib.request
port = 8001
settings_path = Path("/app/data/user/settings/system.json")
try:
settings = json.loads(settings_path.read_text(encoding="utf-8"))
port = int(settings.get("backend_port") or port)
except Exception:
pass
urllib.request.urlopen(f"http://localhost:{port}/", timeout=5).close()
EOF # buildkit
# 2026-08-30 13:23:49 5.09KB 执行命令并创建新的镜像层
RUN /bin/sh -c sed -i 's/\r$//' /app/entrypoint.sh && chmod +x /app/entrypoint.sh # buildkit
# 2026-08-30 13:23:49 5.09KB 执行命令并创建新的镜像层
RUN /bin/sh -c cat > /app/entrypoint.sh <<'EOF'
#!/bin/bash
set -e
echo "============================================"
echo "🚀 Starting DeepTutor"
echo "============================================"
export DEEPTUTOR_IGNORE_PROCESS_ENV_OVERRIDES=1
# Docker is JSON-driven. Ignore runtime env names even if the host or a stale
# Compose environment provides them; the entrypoint re-exports values from
# data/user/settings/*.json below.
for key in \
BACKEND_PORT \
FRONTEND_PORT \
NEXT_PUBLIC_API_BASE_EXTERNAL \
NEXT_PUBLIC_API_BASE \
CORS_ORIGIN \
CORS_ORIGINS \
DISABLE_SSL_VERIFY \
CHAT_ATTACHMENT_DIR \
AUTH_ENABLED \
NEXT_PUBLIC_AUTH_ENABLED \
AUTH_USERNAME \
AUTH_PASSWORD_HASH \
AUTH_TOKEN_EXPIRE_HOURS \
AUTH_COOKIE_SECURE \
POCKETBASE_URL \
POCKETBASE_PORT \
POCKETBASE_EXTERNAL_URL \
POCKETBASE_ADMIN_EMAIL \
POCKETBASE_ADMIN_PASSWORD \
DEEPTUTOR_API_BASE_URL \
DEEPTUTOR_AUTH_ENABLED; do
unset "$key"
done
# Initialize user data directories if empty
echo "📁 Checking data directories..."
echo " Ensuring runtime settings and workspace layout..."
python -c "
from pathlib import Path
from deeptutor.services.setup import init_user_directories
init_user_directories(Path('/app'))
" 2>/dev/null || echo " ⚠️ Directory initialization skipped (will be created on first use)"
# Idempotent: re-chown /app/data so the unprivileged `deeptutor` user (UID 1000)
# owns it. Cheap on no-op; the only first-start cost is one stat per file.
chown -R deeptutor:deeptutor /app/data 2>/dev/null || true
# Optional dependencies (#762). A container is disposable, so anything
# `docker exec … pip install`ed into a running one is gone at the next
# `compose down`. Declare them on the deployment instead and every container
# started from it has them:
#
# environment:
# DEEPTUTOR_EXTRAS: "math-animator,partners"
# DEEPTUTOR_APT_PACKAGES: "ffmpeg"
#
# Both steps are idempotent — a warm container only pays a check — and neither
# is allowed to be fatal: a missing wheel leaves that one feature unavailable,
# exactly as it was before, rather than taking the whole deployment down.
# The pip cache lives on the data volume so a rebuild reuses the downloads it
# already paid for instead of fetching them again.
export PIP_CACHE_DIR="${PIP_CACHE_DIR:-/app/data/.cache/pip}"
mkdir -p "$PIP_CACHE_DIR" 2>/dev/null || true
if [ -n "${DEEPTUTOR_APT_PACKAGES:-}" ]; then
echo "🔧 Ensuring system packages: ${DEEPTUTOR_APT_PACKAGES}"
apt_missing=""
for pkg in $(echo "${DEEPTUTOR_APT_PACKAGES}" | tr ',' ' '); do
dpkg -s "$pkg" >/dev/null 2>&1 || apt_missing="$apt_missing $pkg"
done
if [ -z "$apt_missing" ]; then
echo " ✅ System packages already present"
elif ! (apt-get update -qq && apt-get install -y --no-install-recommends $apt_missing); then
echo " ⚠️ apt-get failed; these packages stay unavailable:$apt_missing"
fi
fi
if [ -n "${DEEPTUTOR_EXTRAS:-}" ]; then
echo "🔧 Ensuring Python extras: ${DEEPTUTOR_EXTRAS}"
python /app/scripts/install_extras.py "${DEEPTUTOR_EXTRAS}" || true
chown -R deeptutor:deeptutor "$PIP_CACHE_DIR" 2>/dev/null || true
fi
echo "⚙️ Loading runtime JSON settings..."
eval "$(python - <<'PY'
import shlex
from deeptutor.services.config import export_runtime_settings_to_env
for key, value in export_runtime_settings_to_env(overwrite=True).items():
print(f"export {key}={shlex.quote(str(value))}")
PY
)"
export BACKEND_PORT=${BACKEND_PORT:-8001}
export FRONTEND_PORT=${FRONTEND_PORT:-3782}
# DEEPTUTOR_API_BASE_URL and DEEPTUTOR_AUTH_ENABLED are exported by the
# export_runtime_settings_to_env eval above (see render_environment in
# deeptutor/services/config/runtime_settings.py). web/proxy.ts reads them at
# request time to rewrite /api/* and /ws/* to the backend and to gate the login
# redirect. Keeping them in the single JSON-backed exporter means the Docker and
# `deeptutor start` paths stay in sync.
echo "📌 API Base URL (proxy): ${DEEPTUTOR_API_BASE_URL:-http://localhost:${BACKEND_PORT}}"
echo "📌 Auth enabled: ${DEEPTUTOR_AUTH_ENABLED:-false}"
echo "📌 Backend Port: ${BACKEND_PORT}"
echo "📌 Frontend Port: ${FRONTEND_PORT}"
echo "============================================"
echo "📦 Configuration loaded from:"
echo " - data/user/settings/system.json"
echo " - data/user/settings/auth.json"
echo " - data/user/settings/integrations.json"
echo " - data/user/settings/model_catalog.json"
echo " - data/user/settings/main.yaml"
echo " - data/user/settings/agents.yaml"
echo "============================================"
# Hand off to supervisord as PID 1. The daemon-level config deliberately omits
# `user=` so supervisord inherits PID 1's UID and stays portable across rootful
# and rootless-keep-id runtimes; children drop to the deeptutor user via
# per-program `user=`. Full rationale lives next to the [supervisord] section
# in the build step that writes /etc/supervisor/supervisord.conf.
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
EOF # buildkit
# 2026-08-30 13:23:49 278.00B 执行命令并创建新的镜像层
RUN /bin/sh -c sed -i 's/\r$//' /app/start-frontend.sh && chmod +x /app/start-frontend.sh # buildkit
# 2026-08-30 13:23:49 278.00B 执行命令并创建新的镜像层
RUN /bin/sh -c cat > /app/start-frontend.sh <<'EOF'
#!/bin/bash
set -e
FRONTEND_PORT=${FRONTEND_PORT:-3782}
FRONTEND_HOST=${FRONTEND_HOST:-0.0.0.0}
echo "[Frontend] 🚀 Starting Next.js frontend on ${FRONTEND_HOST}:${FRONTEND_PORT}..."
export PORT=${FRONTEND_PORT}
export HOSTNAME=${FRONTEND_HOST}
exec node /app/web/server.js
EOF # buildkit
# 2026-08-30 13:23:48 1.61KB 执行命令并创建新的镜像层
RUN /bin/sh -c sed -i 's/\r$//' /app/start-backend.sh && chmod +x /app/start-backend.sh # buildkit
# 2026-08-30 13:23:48 1.61KB 执行命令并创建新的镜像层
RUN /bin/sh -c cat > /app/start-backend.sh <<'EOF'
#!/bin/bash
set -e
BACKEND_PORT=${BACKEND_PORT:-8001}
BACKEND_HOST=${BACKEND_HOST:-0.0.0.0}
echo "[Backend] 🚀 Starting FastAPI backend on ${BACKEND_HOST}:${BACKEND_PORT}..."
# Run uvicorn directly - the application's logging system already handles:
# 1. Console output (visible in docker logs)
# 2. File logging to data/user/logs/ai_tutor_*.log
#
# BACKEND_HOST defaults to 0.0.0.0 (LAN-reachable, matches bridge-mode
# port publishing). Set BACKEND_HOST=127.0.0.1 when running with
# network_mode: host to keep the backend on loopback only.
#
# --ws-max-size: chat attachments travel base64 inside one WS message; derive
# the frame cap from the configured attachment policy (system.json) so uploads
# the policy allows are not severed by uvicorn's 16MB default.
#
# --timeout-keep-alive: the frontend proxy (web/proxy.ts) forwards over Node's
# http.globalAgent, which reaps idle sockets on a 5s timer — identical to
# uvicorn's default, so both ends raced to close the same socket and the loser's
# request died with ECONNRESET (a 500 in the UI). Stay well above the proxy's
# reaper so the client is the only side retiring idle connections.
WS_MAX_SIZE=$(python -c "from deeptutor.services.config import get_ws_max_size; print(get_ws_max_size())" 2>/dev/null || echo 16777216)
KEEP_ALIVE=$(python -c "from deeptutor.services.config import HTTP_KEEP_ALIVE_TIMEOUT; print(HTTP_KEEP_ALIVE_TIMEOUT)" 2>/dev/null || echo 300)
exec python -m uvicorn deeptutor.api.main:app --host ${BACKEND_HOST} --port ${BACKEND_PORT} --no-access-log --ws-max-size ${WS_MAX_SIZE} --timeout-keep-alive ${KEEP_ALIVE}
EOF # buildkit
# 2026-08-30 13:23:48 548.00B 执行命令并创建新的镜像层
RUN /bin/sh -c sed -i 's/\r$//' /etc/supervisor/conf.d/programs.conf # buildkit
# 2026-08-30 13:23:48 548.00B 执行命令并创建新的镜像层
RUN /bin/sh -c cat > /etc/supervisor/conf.d/programs.conf <<'EOF'
[program:backend]
command=/bin/bash /app/start-backend.sh
directory=/app
user=deeptutor
autostart=true
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
environment=PYTHONPATH="/app",PYTHONUNBUFFERED="1"
[program:frontend]
command=/bin/bash /app/start-frontend.sh
directory=/app/web
user=deeptutor
autostart=true
autorestart=true
startsecs=5
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
environment=NODE_ENV="production"
EOF # buildkit
# 2026-08-30 13:23:48 150.00B 执行命令并创建新的镜像层
RUN /bin/sh -c sed -i 's/\r$//' /etc/supervisor/supervisord.conf # buildkit
# 2026-08-30 13:23:48 150.00B 执行命令并创建新的镜像层
RUN /bin/sh -c cat > /etc/supervisor/supervisord.conf <<'EOF'
[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0
pidfile=/tmp/supervisord.pid
[include]
files = /etc/supervisor/conf.d/programs.conf
EOF # buildkit
# 2026-08-30 13:23:48 0.00B 执行命令并创建新的镜像层
RUN /bin/sh -c mkdir -p /etc/supervisor/conf.d # buildkit
# 2026-08-30 13:23:48 35.48MB 执行命令并创建新的镜像层
RUN /bin/sh -c groupadd --system --gid 1000 deeptutor && useradd --system --uid 1000 --gid 1000 --no-create-home --shell /usr/sbin/nologin deeptutor && chown -R deeptutor:deeptutor /app/data /app/web/.next # buildkit
# 2026-08-30 13:23:45 0.00B 执行命令并创建新的镜像层
RUN /bin/sh -c mkdir -p data/user/settings data/memory data/user/workspace/memory data/user/workspace/notebook data/user/workspace/co-writer/audio data/user/workspace/co-writer/tool_calls data/user/workspace/chat/chat data/user/workspace/chat/deep_solve data/user/workspace/chat/deep_question data/user/workspace/chat/deep_research/reports data/user/workspace/chat/math_animator data/user/workspace/chat/_detached_code_execution data/user/logs data/knowledge_bases # buildkit
# 2026-08-30 13:23:45 1.02KB 复制新文件或目录到容器中
COPY requirements.txt ./ # buildkit
# 2026-08-30 13:23:45 7.37KB 复制新文件或目录到容器中
COPY requirements/ ./requirements/ # buildkit
# 2026-08-30 13:23:45 17.57KB 复制新文件或目录到容器中
COPY pyproject.toml ./ # buildkit
# 2026-08-30 13:23:45 66.29KB 复制新文件或目录到容器中
COPY scripts/ ./scripts/ # buildkit
# 2026-08-30 13:23:45 213.38KB 复制新文件或目录到容器中
COPY deeptutor_cli/ ./deeptutor_cli/ # buildkit
# 2026-08-30 13:23:45 8.33MB 复制新文件或目录到容器中
COPY deeptutor/ ./deeptutor/ # buildkit
# 2026-08-30 13:23:45 745.93KB 复制新文件或目录到容器中
COPY /app/web/public/ ./web/public/ # buildkit
# 2026-08-30 13:23:45 15.66MB 复制新文件或目录到容器中
COPY /app/web/.next/static/ ./web/.next/static/ # buildkit
# 2026-08-30 13:23:45 69.59MB 复制新文件或目录到容器中
COPY /app/web/.next/standalone/ ./web/ # buildkit
# 2026-08-30 13:23:25 79.88KB 复制新文件或目录到容器中
COPY /usr/local/bin /usr/local/bin # buildkit
# 2026-08-30 13:23:24 822.75MB 复制新文件或目录到容器中
COPY /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages # buildkit
# 2026-08-30 13:21:15 236.94KB 执行命令并创建新的镜像层
RUN /bin/sh -c ln -sf /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && ln -sf /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx && node --version && npm --version # buildkit
# 2026-08-30 13:21:14 11.94MB 复制新文件或目录到容器中
COPY /usr/local/lib/node_modules /usr/local/lib/node_modules # buildkit
# 2026-08-30 13:21:13 124.84MB 复制新文件或目录到容器中
COPY /usr/local/bin/node /usr/local/bin/node # buildkit
# 2026-08-30 13:21:13 352.88MB 执行命令并创建新的镜像层
RUN /bin/sh -c apt-get update && apt-get install -y --no-install-recommends curl ca-certificates bash git supervisor libgl1 libglib2.0-0 libsm6 libxext6 libxrender1 && rm -rf /var/lib/apt/lists/* # buildkit
# 2026-08-30 13:20:34 0.00B 设置工作目录为/app
WORKDIR /app
# 2026-08-30 13:20:34 0.00B 设置环境变量 PYTHONDONTWRITEBYTECODE PYTHONUNBUFFERED PYTHONIOENCODING MALLOC_ARENA_MAX MALLOC_TRIM_THRESHOLD_ NODE_ENV DEEPTUTOR_IGNORE_PROCESS_ENV_OVERRIDES
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 PYTHONIOENCODING=utf-8 MALLOC_ARENA_MAX=2 MALLOC_TRIM_THRESHOLD_=131072 NODE_ENV=production DEEPTUTOR_IGNORE_PROCESS_ENV_OVERRIDES=1
# 2026-08-30 13:20:34 0.00B 添加元数据标签
LABEL maintainer=DeepTutor Team description=DeepTutor: AI-Powered Personalized Learning Assistant
# 2026-08-25 09:20:06 0.00B 设置默认要执行的命令
CMD ["python3"]
# 2026-08-25 09:20:06 36.00B 执行命令并创建新的镜像层
RUN /bin/sh -c set -eux; for src in idle3 pip3 pydoc3 python3 python3-config; do dst="$(echo "$src" | tr -d 3)"; [ -s "/usr/local/bin/$src" ]; [ ! -e "/usr/local/bin/$dst" ]; ln -svT "$src" "/usr/local/bin/$dst"; done # buildkit
# 2026-08-25 09:20:06 42.41MB 执行命令并创建新的镜像层
RUN /bin/sh -c set -eux; savedAptMark="$(apt-mark showmanual)"; apt-get update; apt-get install -y --no-install-recommends dpkg-dev gcc gnupg libbluetooth-dev libbz2-dev libc6-dev libdb-dev libffi-dev libgdbm-dev liblzma-dev libncursesw5-dev libreadline-dev libsqlite3-dev libssl-dev make tk-dev uuid-dev wget xz-utils zlib1g-dev ; wget -O python.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz"; echo "$PYTHON_SHA256 *python.tar.xz" | sha256sum -c -; wget -O python.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc"; GNUPGHOME="$(mktemp -d)"; export GNUPGHOME; gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$GPG_KEY"; gpg --batch --verify python.tar.xz.asc python.tar.xz; gpgconf --kill all; rm -rf "$GNUPGHOME" python.tar.xz.asc; mkdir -p /usr/src/python; tar --extract --directory /usr/src/python --strip-components=1 --file python.tar.xz; rm python.tar.xz; cd /usr/src/python; gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; ./configure --build="$gnuArch" --enable-loadable-sqlite-extensions --enable-optimizations --enable-option-checking=fatal --enable-shared $(test "${gnuArch%%-*}" != 'riscv64' && echo '--with-lto') --with-ensurepip ; nproc="$(nproc)"; EXTRA_CFLAGS="$(dpkg-buildflags --get CFLAGS)"; LDFLAGS="$(dpkg-buildflags --get LDFLAGS)"; LDFLAGS="${LDFLAGS:-} -Wl,--strip-all"; make -j "$nproc" "EXTRA_CFLAGS=${EXTRA_CFLAGS:-}" "LDFLAGS=${LDFLAGS:-}" ; rm python; make -j "$nproc" "EXTRA_CFLAGS=${EXTRA_CFLAGS:-}" "LDFLAGS=${LDFLAGS:-} -Wl,-rpath='\$\$ORIGIN/../lib'" python ; make install; cd /; rm -rf /usr/src/python; find /usr/local -depth \( \( -type d -a \( -name test -o -name tests -o -name idle_test \) \) -o \( -type f -a \( -name '*.pyc' -o -name '*.pyo' -o -name 'libpython*.a' \) \) \) -exec rm -rf '{}' + ; ldconfig; apt-mark auto '.*' > /dev/null; apt-mark manual $savedAptMark; find /usr/local -type f -executable -not \( -name '*tkinter*' \) -exec ldd '{}' ';' | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); printf "*%s\n", so }' | sort -u | xargs -rt dpkg-query --search | awk 'sub(":$", "", $1) { print $1 }' | sort -u | xargs -r apt-mark manual ; apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; apt-get dist-clean; export PYTHONDONTWRITEBYTECODE=1; python3 --version; pip3 install --disable-pip-version-check --no-cache-dir --no-compile 'setuptools==79.0.1' 'wheel==0.46.3' ; pip3 --version # buildkit
# 2026-08-25 09:11:45 0.00B 设置环境变量 PYTHON_SHA256
ENV PYTHON_SHA256=91bcdebfdde239a003ae93738a7fce0f9230fee5c4bc2b86f6e6e8c6f98aabe8
# 2026-08-25 09:11:45 0.00B 设置环境变量 PYTHON_VERSION
ENV PYTHON_VERSION=3.11.16
# 2026-08-25 09:11:45 0.00B 设置环境变量 GPG_KEY
ENV GPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696D
# 2026-08-25 09:11:45 3.81MB 执行命令并创建新的镜像层
RUN /bin/sh -c set -eux; apt-get update; apt-get install -y --no-install-recommends ca-certificates netbase tzdata ; apt-get dist-clean # buildkit
# 2026-08-25 09:11:45 0.00B 设置环境变量 LANG
ENV LANG=C.UTF-8
# 2026-08-25 09:11:45 0.00B 设置环境变量 PATH
ENV PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# 2026-08-24 08:00:00 78.64MB
# debian.sh --arch 'amd64' out/ 'trixie' '@1787529600'
镜像信息
{
"Id": "sha256:3a5953542413e104f8b8cf7c94287ada4df823f06db85ae29d9b26ec1ae845ab",
"RepoTags": [
"ghcr.io/hkuds/deeptutor:1.6.1",
"swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/hkuds/deeptutor:1.6.1"
],
"RepoDigests": [
"ghcr.io/hkuds/deeptutor@sha256:52ae86299c4bbd3eeea58f41c7123f93c26a4e41966b611fb7298460c8c12e24",
"swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/hkuds/deeptutor@sha256:825cea1ddd721df9328ca4881500d5d7a03edbf335ca201f37e42249bfa5a559"
],
"Parent": "",
"Comment": "buildkit.dockerfile.v0",
"Created": "2026-08-30T05:23:49.334603087Z",
"Container": "",
"ContainerConfig": null,
"DockerVersion": "",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"3782/tcp": {},
"8001/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"LANG=C.UTF-8",
"GPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696D",
"PYTHON_VERSION=3.11.16",
"PYTHON_SHA256=91bcdebfdde239a003ae93738a7fce0f9230fee5c4bc2b86f6e6e8c6f98aabe8",
"PYTHONDONTWRITEBYTECODE=1",
"PYTHONUNBUFFERED=1",
"PYTHONIOENCODING=utf-8",
"MALLOC_ARENA_MAX=2",
"MALLOC_TRIM_THRESHOLD_=131072",
"NODE_ENV=production",
"DEEPTUTOR_IGNORE_PROCESS_ENV_OVERRIDES=1"
],
"Cmd": null,
"Healthcheck": {
"Test": [
"CMD-SHELL",
"python /app/healthcheck.py"
],
"Interval": 30000000000,
"Timeout": 10000000000,
"StartPeriod": 60000000000,
"Retries": 3
},
"Image": "",
"Volumes": null,
"WorkingDir": "/app",
"Entrypoint": [
"/app/entrypoint.sh"
],
"OnBuild": null,
"Labels": {
"description": "DeepTutor: AI-Powered Personalized Learning Assistant",
"maintainer": "DeepTutor Team",
"org.opencontainers.image.created": "2026-08-30T05:20:28.344Z",
"org.opencontainers.image.description": "DeepTutor: Lifelong Personalized Tutoring. https://deeptutor.info/.",
"org.opencontainers.image.licenses": "Apache-2.0",
"org.opencontainers.image.revision": "ce7c2dc2dc6b7ae3063e81be29608bdba4723a25",
"org.opencontainers.image.source": "https://github.com/HKUDS/DeepTutor",
"org.opencontainers.image.title": "DeepTutor",
"org.opencontainers.image.url": "https://github.com/HKUDS/DeepTutor",
"org.opencontainers.image.version": "1.6.1"
}
},
"Architecture": "amd64",
"Os": "linux",
"Size": 1567719847,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/61c37392708b5f80ee646d9cb9511acf4685d6c2df96fab0cac0c4e4456f1825/diff:/var/lib/docker/overlay2/405bf6624da94bdb91cbf68d9f6d254282a90ab67dd14c290b39b81d60fd4566/diff:/var/lib/docker/overlay2/59249cc1cc07d160a5d8132ee0cbad3835bc26f3fdda651421022990ba144495/diff:/var/lib/docker/overlay2/f74b552b9c8f1f163050462c4ed2fc93ee3f1cbed1a74dcebf57a483ac7d5600/diff:/var/lib/docker/overlay2/981983ac3cbdc71b24e5bf55adc03133bba3297126589f235b23ddf4e8095327/diff:/var/lib/docker/overlay2/ed903118f33254da1d4d8236b618812220f3411d29d245c580e442d36f733d1d/diff:/var/lib/docker/overlay2/3a657e50b8a05992ddb72218a0bca833b03e54005d87c21b275d6ff6d0a74a18/diff:/var/lib/docker/overlay2/b9db1d18514a7fb114c064d02062ca4bfdf181a1a4ce234d267e90387df8ba5a/diff:/var/lib/docker/overlay2/f7339f3cbc9ff8049c2c088ddec5fc75abd411beab9673b4330dbd4cbe9e4fed/diff:/var/lib/docker/overlay2/ebd91663e60ef4424c82b8a560f8b228b53fd8a874907cbf8bc9949adde8e2ca/diff:/var/lib/docker/overlay2/4ed5a77a639fc06900ebc2908680e4a4a4a34ec452412fd820944efa797fad4e/diff:/var/lib/docker/overlay2/78267ce73c500d04015a0bb3e29e379800740a8315357bfc81d347e4fe7e9908/diff:/var/lib/docker/overlay2/51dce864b4ee5c60ad4833e703570c19c3f3880cfcc372cd1a86476b160b930e/diff:/var/lib/docker/overlay2/cfe0a6ef83e589272ac09ab5650f2cb5cfe9ed86c9a582346e40ffd2b5efdd81/diff:/var/lib/docker/overlay2/964a3e3079f14762a0fa9f10efabaaeb37a4a355752171abc7fe4ac4147c7e3b/diff:/var/lib/docker/overlay2/3b0ca6d48dd0e6bbc8ce283231aca013435c6075394a55837a6a81e2910cb265/diff:/var/lib/docker/overlay2/f74730bfbacc436292f37092a6fbbb3f6cb033c02ddd2935744191966995dc90/diff:/var/lib/docker/overlay2/2f8699aa8e33ee3f32a97be164336a1313bea4689c7988d2f6f5147db60cdea3/diff:/var/lib/docker/overlay2/889a659fd6e21b34a0fabba2eb80572b524c182448d295ce5513d6864cdb3839/diff:/var/lib/docker/overlay2/6cff3085f2ca03dfcfc04b30bf5c57d71aad97d083bb7f2ba71406dd6dda5b85/diff:/var/lib/docker/overlay2/85d96efe01d712c667d854820096ddcdbfe06e834e0793f56109bc849fe68097/diff:/var/lib/docker/overlay2/c17a75a8390dff1be8c482eefe111c2cd51ad963ebdf19623805fc58405ac337/diff:/var/lib/docker/overlay2/bf9a74e35918bc81febdbad14c92c704cce3324cfec560451ac43688e1fbcab4/diff:/var/lib/docker/overlay2/018ef561aba95cee868b5285779bf6518ae88d7070188c23a2b1b33cca46470b/diff:/var/lib/docker/overlay2/073dc152da6b0ab533af5287d94d33b27042ead5753b5f7a7da24aced1d3a857/diff:/var/lib/docker/overlay2/12ef7e14da66c2340b1bca948df2e07cda615090592e234ef3967d74ee931756/diff:/var/lib/docker/overlay2/91e964c1b134b31095570be20675428ee5f3f06612c13ae7fa222bf8b32ab731/diff:/var/lib/docker/overlay2/a6bb991e7e31abe0a309e4b476b499712000c85415f26d976780accb29cf4506/diff:/var/lib/docker/overlay2/b780820f685ebb99f545c50c99b34acd313e24f3c6231c431694da882c2ac9f2/diff:/var/lib/docker/overlay2/1fd788734b74a9e67274d4e3c9a2e30a6a9166798a5360caa1f92c01c96d0949/diff:/var/lib/docker/overlay2/bc401619f1e02e81a290e8a090591306219e9543fbcc2279b9336b8bef6ad793/diff:/var/lib/docker/overlay2/19f1d37d161ff5a1d594a40267f4a71723a958ea2b9756869a36357673bbe328/diff:/var/lib/docker/overlay2/c2a696dacdcd5b8480d9f883645aef5d7f18bb808ab62a8bfbe4920c17c72905/diff",
"MergedDir": "/var/lib/docker/overlay2/e40c96bbf078065268caec9fe7ae545474740e03ab16de811b01b36defe91681/merged",
"UpperDir": "/var/lib/docker/overlay2/e40c96bbf078065268caec9fe7ae545474740e03ab16de811b01b36defe91681/diff",
"WorkDir": "/var/lib/docker/overlay2/e40c96bbf078065268caec9fe7ae545474740e03ab16de811b01b36defe91681/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:411a86676185cb54d695a805b238194a64e9b77e0c723f3802fbb87d333ea0b3",
"sha256:3077d73e91e85d4b414497c637c5daaf95b238aea0e5c254b4408cb1678e1867",
"sha256:99dd0251c41dae18863990241cd40c973d6619290e6aeb4545088b7958ec849e",
"sha256:34d8e1a7bf9edb9961a4b10d30ea5ce382418c37c752860b7bd9a2ced8253d39",
"sha256:9a9541f25978aa77625c1311611abbc1ce3f5b5aec54732ba2533d2d4b2d700b",
"sha256:b870fa767b971b9fc1e927ea50455ab7a9f4feab62bc5d6e4fbb763c68ac1ba4",
"sha256:90c4c58b9f00fb8e2faf024d5da58d312ead9b558350cc9fb996c1db86651160",
"sha256:c4757b5f8d33deec1bfab68d7c19c893cba48e2327d91af319fc8d38fdd741c1",
"sha256:966e8f4f9a21ebdfc793e271e78c788d4da54349b352ada42864831c6eead5ca",
"sha256:1ff764098dfcaad9398ea2406b65c8f0f8d173ef43e9f073774d137ee8ae4648",
"sha256:8ed03dd8ad00fb1947a481d6ffd6680fc08ed1a3909c3534e9c934c752d49ac9",
"sha256:9e2558be74fffc820cecca9691b328c16d0c48a5e1b2598f8f617a3224f3906c",
"sha256:bb49c8d1d26fa8b99291ca0802bc55c9baf6252a0c22221ade3a5c3ffd8a4bfe",
"sha256:b40b0b59cd7c4e2ceef713d8e8e07de0c504e19c6b6167850164f1f3e84cb8ec",
"sha256:ba408af0e4a34ce6516d50e6e1d90f29461b041b41eca3f632870490da0e0dcb",
"sha256:96fbab7b17f4a6fa5caa8f4b1163085ae628172f1dea90462183c12f02727c10",
"sha256:11ba03ba5dc23aff49e41f48be5cd39921e9173482f8a999bddbb0146197741f",
"sha256:ac0ed3a60f4d3c8746b55270de6ba14dcff5b8800f11667b608f26c43a699adc",
"sha256:357bbd3e18358daadd092cbef0aceb0bd0334eaea943cb72a0611367889f900e",
"sha256:0d4d7307b987c09e81e1d2fd408c6eb57eb4aae1c74871c0be44fd5eab0a5693",
"sha256:a707bce9f95bd626addcd615eacefa158d473c2f282933199725589011d17316",
"sha256:d52ba973c8bfbd2c3cd400158a61289de4da249cb945c8a0ccf01e5acfcb86ab",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
"sha256:b9d773ebcc05bc8faf55be1dcbf5ceab8939d0e8c067e858d17e284a63b1d1c7",
"sha256:d745270ff99928bf3b0a389de317d37380bfe67ead064d8ad726437d888983a3",
"sha256:2d611b4f6f06ac3a6583fff627b97e8f308d6257e75f6756eff8d80ea92592f4",
"sha256:2d611b4f6f06ac3a6583fff627b97e8f308d6257e75f6756eff8d80ea92592f4",
"sha256:35cee14f98fa08d93b35c03dbdac7f9e882fd7d5a17ab8eaeece3a41c9647dd9",
"sha256:58436607b57ba5cce907e334c50356a73fdf96be6919b59205110d939eb6346a",
"sha256:91689a8a222dab508d89dd70440a62d814e2f3d19e6c38ee912e601699e26cbb",
"sha256:c6dfc127cb63c27ac0f3585437be9a92d1f5ba908b0e70e1fce738da788921f1",
"sha256:6e0a03dc8aabfbb5560762abbe7516f04032813fbcf0bb97d141202f40277a6e",
"sha256:b926678ad707dec0a7930ced98bee4dd0edde3eac3cf90d0ba71f8912d1de4c7",
"sha256:1df2ff0c413958ba49b53f0555714dc509acae8137291330d02bb7998494e1cc"
]
},
"Metadata": {
"LastTagTime": "2026-08-30T18:57:07.148783168+08:00"
}
}