-
Notifications
You must be signed in to change notification settings - Fork 0
STACK
Mike Crowe edited this page Jul 31, 2026
·
5 revisions
Analysis Date: 2026-07-31
Primary:
- Python 3.12+ — all host CLI logic (
src/harnessed/). Minimum enforced viarequires-python = ">=3.12"inpyproject.toml.
Secondary:
- Bash — recipe install scripts (
catalog/recipes/*/install.sh), container entrypoints (catalog/base/harnessed-start,catalog/base/egress-firewall.sh), and allsetup.scriptbodies in the catalog. Linted byvalidate_install_script/validate_setup_scriptinsrc/harnessed/schema.py. - Dockerfile — agent base images (
catalog/base/Dockerfile.harnessed-*) and recipe derived images. Pin-validated byvalidate_pininsrc/harnessed/schema.py. - JavaScript/TypeScript — the hatago MCP hub (
@drmikecrowe/hatago-mcp-hub) and any recipe that installs annpm:tool. Not authored in this repo; pulled from npm registry at container build time.
Host:
- Python 3.12 (managed via
astral-sh/setup-uvin CI, or host Python for local dev) - Package manager: uv —
uv sync --extra devinstalls the project + dev deps.uv run pytestexecutes tests. - Lockfile:
uv.lock(present, tracks all transitive deps) -
mise.tomlsetsUV_PROJECT_ENVIRONMENTto~/.local/share/harnessed/venvs/<branch>/.venv— one venv per git branch, stored outside the repo to prevent corruption by container bind-mounts.
Container (podman pod, built by harnessed):
- Base image:
ubuntu:24.04 - Container user:
harnessed(UID 1000, renamed from ubuntu;--userns=keep-idmaps host user) - Tool version manager: mise (baked into the base image)
- Container runtime tools (installed in
catalog/base/Dockerfile.harnessed-baseviamise use -g):-
node@22(LTS) +pnpm@11(npm package manager;mise settings set npm.package_manager pnpmroutes allnpm:installs through pnpm) -
npm@11.18.0(self-upgraded from node-bundled 10.9.8 to fix HIGH CVEs in transitive deps) python@3.12bun@1.2rust@1.87go@1.24-
fd,ripgrep(file search tools) -
gh@2.96.0(GitHub CLI) -
uv,osv-scanner(added in scanner layer)
-
- Container runtime: podman — driven from the host via
subprocessinsrc/harnessed/launcher.py. No daemon socket; rootless podman only (--userns=keep-id).
Core:
-
Typer
>=0.12,<1.0— CLI framework for theharnessedandharnessed-toolsentrypoints. Defined inpyproject.toml; app objects live insrc/harnessed/launcher.py(app) andsrc/harnessed/cli.py.
Testing:
-
pytest
>=8— test runner. Config:[tool.pytest.ini_options]inpyproject.toml,testpaths = ["tests"]. Run:mise exec -- uv run pytest -q. - pytest-cov — coverage plugin (no explicit config block; coverage not enforced in CI).
-
jsonschema
>=4— JSON Schema validation used in tests and catalog scanning.
Build/Dev:
-
setuptools
>=68— build backend (pyproject.toml[build-system]). Ships thecatalog/tree inside the wheel via thesrc/harnessed/catalogsymlink +package-data = ["catalog/**/*"]. -
mise (host) — manages the per-branch
.venv, providesmise exec --wrapper. Tasks defined inmise.toml:docs,docs-check,pull-and-build. -
uv tool install (host) — installs
harnessedas an editable uv tool during dev (mise tasks.pull-and-buildrunsuv tool install --editable .).
Critical (core functionality depends on these):
-
ruamel.yaml>=0.18,<0.19— YAML parser used throughout for catalog loading (schema.py,assemble.py,update.py). Chosen for round-trip comment preservation and strict typing over PyYAML. -
rich>=14,<15— all terminal output, warnings, and the_WarnCountingConsolesubclass inlauncher.pythat counts warning lines beforeos.execvphands off the TTY. -
typer>=0.12,<1.0— CLI command tree, argument parsing, and help text. Both entrypoints (harnessed,harnessed-tools) are Typer apps. -
pip-audit==2.10.1— pinned exactly (not a range). Baked into the base image viauv tool install pip-auditand invoked by theharnessed-scanscript inside every built stack image for credential-free Python supply-chain checks.
Infrastructure:
-
jsonschema>=4— validates catalog YAML against JSON Schema definitions underschemas/. Used in tests and incli.py(harnessed-tools scan).
Container-side (baked into harnessed-base, not Python deps):
-
@drmikecrowe/hatago-mcp-hub@0.1.2— the in-container MCP aggregator hub (fork of@himorishige/hatago-mcp-hub). Installed viapnpm add -ginDockerfile.harnessed-base. See INTEGRATIONS.md. -
snyk— token-gated supply-chain scanner, baked viapnpm add -g snyk. -
socket@1.1.143— token-gated supply-chain scanner, baked viapnpm add -g socket@1.1.143. Pinned to v1 to lock supply-chain defaults. -
osv-scanner— credential-free vuln scanner, baked viamise use -g osv-scanner.
Environment and secrets:
- Host-level secrets live in
~/.config/harnessed/.env.schema, resolved via varlock (varlock load --format json) usingop://1Password references. See.env.schema.exampleat the repo root for the DSL and all supported keys. - Per-project secrets live in
<project>/.env.schema(varlock) or<project>/.env(raw key=value). Both are layered by_resolve_launch_secrets()inlauncher.pyand injected into podman via--env-file(temp files unlinked after launch — secrets never linger on disk). - When varlock is absent, harnessed falls back to reading
~/.config/harnessed/.envliterally.
Key config files and their roles:
| File | Role |
|---|---|
pyproject.toml |
Python project metadata, pinned runtime deps, dev extras, setuptools package-data config, pytest testpaths |
mise.toml |
Per-branch venv path (UV_PROJECT_ENVIRONMENT), dev task definitions (docs, pull-and-build) |
uv.lock |
Full dependency lockfile (all transitive deps; committed) |
~/.config/harnessed/.env.schema |
User-global secrets (SNYK_TOKEN, SOCKET_SECURITY_API_KEY, corp proxy CA path, etc.) |
~/.config/harnessed/catalog/ |
User overlay catalog — wins over repo catalog/ on name clash |
catalog/base/extra-tools.txt |
Staged at build time from extra-tools.default.txt + user edits; additional mise tools baked into harnessed-base
|
catalog/base/pnpm/config.yaml |
Managed pnpm supply-chain config; COPY'd into the base image before any pnpm invocation |
$XDG_DATA_HOME/harnessed/profiles/ |
Generated (assembled) stack profiles — never in the repo |
Catalog search path (two roots, first wins on clash):
-
~/.config/harnessed/catalog/(user overlay) -
catalog/(repo, shipped inside the wheel viasrc/harnessed/catalogsymlink)
paths.harnessed_home() resolves through the symlink to give podman a real directory as the build context — podman rejects context symlinks that escape the context root.
Volume and profile naming:
- Assembled profiles:
$XDG_DATA_HOME/harnessed/profiles/<stack>/<harness>/ - Tool volumes:
harnessed-tools-<harness>-<stack>(~/.localinside container) - Config volumes:
harnessed-cfg-<harness>-<stack>(~/.claudeinside container) - pnpm metadata cache:
harnessed-pnpm-meta(podman named volume,--sharing=shared) - uv cache:
harnessed-uv(podman named volume) - mise cache:
harnessed-mise(podman named volume,--sharing=locked)
Start Here
Guides
- Recipe authoring
- Service authoring
- Stacks
- Extending stacks (proposed)
- Recipe catalog
- System prompt & rules (proposed)
- Secrets
- AWS SSO
- Pulumi (host login forwarding)
- Egress & exposing services
- Container filesystem
- Git hooks
- Troubleshooting
- Pin management (harnessed update)
Codebase Map
Planning & Roadmap
- open work: GitHub Issues
Research & Prompts
- research/ (home-folder requirements per harness, browse in-repo)
- prompts/ (reusable prompt templates, browse in-repo)