mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-11-13 09:13:30 +01:00
docs(ai): move agent rules to ai/skills and add an AI development sandbox (#1498)
The sandbox provides an isolated Docker environment for AI-assisted development (OpenCode + Claude Code + Qwen Code) against the local AkkudoktorEOS repository. The sandbox does not protect the repository from harmful changes by the agent. It limits what a misled agent can reach on the machine, but the agent can still write any change into the repository. Primary security defence is to check every change before it is run on the host. Same holds for opening a pull request or updating one. Additional changes - Reduce AGENTS.md to the project summary, baseline rules, completion checklist, and an index of the skills. - Add skills for the Python environment and REST API error responses. - Add the development guide page docs/develop/ai.md: the AI coding workflow, how agents get the project rules from AGENTS.md and ai/skills, and how to use the sandbox (start, agent logins, project commands with a virtual environment outside the workspace, running EOS and EOSdash, GitHub access, persistent data, security model). - Add a checklist for reviewing agent changes before pushing, starting with .git, which stays writable in the sandbox. - Check out all text files with LF line endings (.gitattributes), also on Windows with core.autocrlf, so the sandbox scripts run in the container and git in the container agrees with git on the host. Batch files keep CRLF. - Publish the ports of ai/compose.yaml on request (AI_SANDBOX_PORTS=1). docker compose run publishes them only with --service-ports. Off by default, so that several sandboxes can run in parallel. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
628226d3d9
commit
2245976de4
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env bash
|
||||
# ai/entrypoint.sh
|
||||
# Initializes the AI sandbox container on startup, then execs the command.
|
||||
# Installed to /usr/local/bin (image layer, root-owned) so it cannot be
|
||||
# shadowed or modified via the persistent /home/dev volume.
|
||||
set -euo pipefail
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Hardening
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# The home directory is a persistent volume, so files written in one session
|
||||
# are still there in the next. The steps below remove the most direct ways to
|
||||
# make such a file run automatically. They do not cover everything (agent
|
||||
# settings and hooks in ~/.claude, ~/.gitconfig, the venv in ~/.cache):
|
||||
# reset the volume (docker volume rm akkudoktor-ai-home) if a session may have
|
||||
# been misled.
|
||||
|
||||
# Strip exec bits from $HOME, except from the uv cache and the virtual
|
||||
# environment in ~/.cache, which must stay executable. A program planted in
|
||||
# e.g. ~/.local/bin (first on PATH) then does not run in the next session.
|
||||
# Runs as the non-root `dev` user, so it can only touch its own files.
|
||||
strip_exec_bits() {
|
||||
find /home/dev \
|
||||
-path /home/dev/.cache -prune -o \
|
||||
-path /home/dev/.local/share/uv -prune -o \
|
||||
-type f -perm /111 -exec chmod a-x -- {} + 2>/dev/null || true
|
||||
}
|
||||
|
||||
strip_exec_bits
|
||||
|
||||
# Reset the shell startup files on every start, so a startup file written in
|
||||
# a previous session cannot inject commands into the next shell.
|
||||
# Customizations of these files do not persist.
|
||||
reset_shell_startup_files() {
|
||||
local f
|
||||
for f in .bashrc .profile .bash_logout; do
|
||||
# Remove first: cp onto a planted symlink would write to its target.
|
||||
rm -f -- "$HOME/$f"
|
||||
if [[ -f "/etc/skel/$f" ]]; then
|
||||
cp -- "/etc/skel/$f" "$HOME/$f"
|
||||
fi
|
||||
done
|
||||
rm -f -- "$HOME/.bash_profile" "$HOME/.bash_login" \
|
||||
"$HOME/.zshrc" "$HOME/.zprofile" "$HOME/.zshenv" "$HOME/.zlogin"
|
||||
}
|
||||
|
||||
reset_shell_startup_files
|
||||
|
||||
# NOTE: /workspace is the user's own repository. The entrypoint must never
|
||||
# modify it — in particular .git/hooks is left untouched (pre-commit etc.).
|
||||
# .git is writable in the sandbox, so a previous session may have added hooks.
|
||||
# Warn about the active hooks, so they get noticed. This is no protection: the
|
||||
# review checklist in docs/develop/ai.md checks .git on the host.
|
||||
warn_git_hooks() {
|
||||
local hooks_dir
|
||||
# Honors core.hooksPath and worktrees; falls back to the default location.
|
||||
hooks_dir="$(git -C /workspace rev-parse --path-format=absolute --git-path hooks 2>/dev/null)" \
|
||||
|| hooks_dir="/workspace/.git/hooks"
|
||||
[[ -d "$hooks_dir" ]] || return 0
|
||||
|
||||
local hooks=()
|
||||
while IFS= read -r -d '' f; do
|
||||
hooks+=("$f")
|
||||
done < <(find "$hooks_dir" -maxdepth 1 -type f ! -name '*.sample' -print0 2>/dev/null)
|
||||
[[ ${#hooks[@]} -gt 0 ]] || return 0
|
||||
|
||||
{
|
||||
echo "WARNING: active git hooks found in $hooks_dir"
|
||||
echo " They run on the host too. Verify they are expected:"
|
||||
local f
|
||||
for f in "${hooks[@]}"; do
|
||||
printf ' %s (modified %s)\n' \
|
||||
"$(basename -- "$f")" \
|
||||
"$(date -r "$f" '+%Y-%m-%d %H:%M' 2>/dev/null || echo '?')"
|
||||
done
|
||||
} >&2
|
||||
}
|
||||
|
||||
warn_git_hooks || true
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Initialization
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Prepare a persistent uv cache inside the home volume.
|
||||
export UV_CACHE_DIR="/home/dev/.cache/uv"
|
||||
mkdir -p "$UV_CACHE_DIR"
|
||||
|
||||
# Configure git identity if provided via environment.
|
||||
if [[ -n "${GIT_USER_NAME:-}" ]]; then
|
||||
git config --global user.name "$GIT_USER_NAME"
|
||||
fi
|
||||
if [[ -n "${GIT_USER_EMAIL:-}" ]]; then
|
||||
git config --global user.email "$GIT_USER_EMAIL"
|
||||
fi
|
||||
|
||||
# If gh is already authenticated, wire git to use it as a credential helper.
|
||||
if gh auth status &>/dev/null; then
|
||||
gh auth setup-git || true
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
Reference in New Issue
Block a user