-
Notifications
You must be signed in to change notification settings - Fork 0
INTEGRATIONS
Analysis Date: 2026-07-31
- What: All container build and launch operations.
-
How connected:
subprocesscalls insrc/harnessed/launcher.py. The runtime binary is resolved viashutil.which("podman")(withCONTAINER_RUNTIMEenv override). No daemon socket; rootless podman only. -
Key operations:
podman build(base + derived images),podman pod create/start/kill,podman run(install scripts in volume-write containers),podman exec(setup scripts post-start),podman volume create/inspect/rm. -
Build secrets: Corporate proxy CA injected via
--secret id=corp_proxy_ca,src=<cert>(never baked into image layers). Resolved by_corp_proxy_ca_secret_args()inlauncher.py. -
Egress firewall:
catalog/base/egress-firewall.shruns inside the container after setup scripts complete; appliesiptablesrules to close outbound network.
-
Package:
@drmikecrowe/hatago-mcp-hub@0.1.2(maintained fork of upstream@himorishige/hatago-mcp-hub, which stopped at 0.0.16). -
Where installed: Baked into
catalog/base/Dockerfile.harnessed-baseviapnpm add -g "@drmikecrowe/hatago-mcp-hub@0.1.2". All harness images inherit it — there is no separate hatago image. -
Role: In-container MCP bus (hatago-consolidation). Runs as a process inside the agent container (started by
harnessed-start). Aggregates all recipe MCP servers (stdio children baked into the image) behind a single Streamable-HTTP endpoint — the harness's.mcp.jsonpoints only at this one URL. -
Endpoint:
paths.hatago_endpoint()(default port frompaths.hatago_port(), overridden byHATAGO_PORT). Probed bycapability.pyatHATAGO_ENDPOINTfor the capability test. -
Config: Assembled per-stack as
hatago.config.jsonin the profile dir byemit.write_hatago_config(), called fromsrc/harnessed/assemble.py. - Transport constraint: Streamable-HTTP only. SSE is deprecated and rejected.
-
What: Resolves
op://1Password references in.env.schemafiles into plain env vars, injected into the container via--env-file(never written to the repo or a profile). -
Client code:
_varlock_resolve()and_varlock_resolve_env_file()insrc/harnessed/launcher.py. Also used host-side by_resolve_host_launch_secrets(). -
DSL: varlock's
.env.schemaformat with@plugin(@varlock/1password-plugin@1.2.0)and@initOp(allowAppAuth=true). See.env.schema.exampleat the repo root. -
Layering:
_resolve_launch_secrets()stacks (1)~/.config/harnessed/.env.schema(global) then (2)<project>/.env.schemaor<project>/.env(project). Podman's--env-fileis last-wins, so project values override global. -
Fallback: If varlock is not on PATH, harnessed reads
~/.config/harnessed/.envliterally (no resolution). -
Security: Resolved env-files are written to temp paths (mode 0600) and unlinked immediately after
podman run/os.execvp. They are never committed, never baked, and never written to a profile.
-
CLI:
op(1Password CLI). Best-effort install incatalog/base/Dockerfile.harnessed-basefromdownloads.1password.com. A failed download warns and continues — the only capability lost is 1Password-based SSH commit signing (op-ssh-sign). -
SSH agent socket:
launcher._op_agent_socket()returns~/.1password/agent.sock(Linux) or the macOS Group Containers path. Forwarded into the container via--volume <sock>:<ctr_sock>:rowhen the socket exists. Controlled by_ssh_agent_args(). -
Shim:
catalog/base/opis a shim placed at/usr/local/bin/op(ahead of/usr/bin/opon PATH). Removed from the image if the realopdid not install. -
Secret references in varlock DSL:
op://Private/SNYK_API_TOKEN/credential, etc. See.env.schema.example.
src/harnessed/update.py implements harnessed update, which queries live registries to detect stale pins in the catalog. Three registry backends, each called via urllib.request.urlopen (no third-party HTTP client):
| Backend prefix | Registry URL queried | What is resolved |
|---|---|---|
npm: |
https://registry.npmjs.org/{name} |
npm package release list and dates |
pipx: |
https://pypi.org/pypi/{name}/json |
PyPI package release list and dates |
github: / aqua |
https://api.github.com/repos/{repo}/releases?per_page=100 |
GitHub release list and dates |
-
mise registry:
mise registryis shelled out to resolve bare tool names (e.g.pulumi) to their aqua/ubi/github backing repo, then the GitHub API handles versioning. Used in.github/workflows/pin-check.ymlwhich installs mise viajdx/mise-action@v2. -
Minimum release age: Default 7 days (
DEFAULT_MINIMUM_RELEASE_AGE_MINUTES = 10080). A release younger than this window is not offered — the newest version that IS old enough is offered instead (modelled on pnpm'sminimumReleaseAge). -
Held pins:
install.holdinrecipe.yamlorhold:on atools:entry marks a pin manual-only. Held pins are listed in the report but never enter the bump set and never fail--check.
Four scanners are baked into harnessed-base and invoked by catalog/base/harnessed-scan as the final layer of every assembled stack image (emit.write_derived_dockerfile). They run in two tiers:
Credential-free (every build):
-
osv-scanner — checks installed packages against the OSV vulnerability database. Installed via
mise use -g osv-scanner. -
pip-audit — audits Python packages via
pip-audit. Installed viauv tool install pip-audit(pinned topip-audit==2.10.1inpyproject.toml).
Token-gated (only in harnessed rescan, never during harnessed build):
-
snyk — deep supply-chain scan. Token:
SNYK_TOKEN(from 1Password via varlock, keyop://Private/SNYK_API_TOKEN/credential). Installed viapnpm add -g snyk. -
socket
@1.1.143— socket.dev supply-chain scan. Token:SOCKET_SECURITY_API_KEY. Installed viapnpm add -g socket@1.1.143. Pinned to v1.
harnessed rescan <image> resolves scanner tokens on the host from ~/.config/harnessed/.env{,.schema}, injects them as container env, and writes its report to <profile>/scan-report.json. The build's own scan layer is deliberately credential-free.
-
What: A per-project
dolt sql-serverinstance, serving MySQL protocol on a Unix socket. Backs the Beads (bd) issue tracker. -
Service definition:
catalog/services/beads-server/service.yaml—scope: project, socket-backed. -
Image:
catalog/services/beads-server/Dockerfile— built FROMubuntu:24.04, installsdoltandbdviamise use -g "github:dolthub/dolt@${DOLT_VERSION}" "github:gastownhall/beads@${BEADS_VERSION}". -
Data dir:
/datainside the container, bind-mounted from the project's.beads/directory. Socket:<project>/.beads/run/mysql.sock(or equivalent per placement). -
Client env:
HARNESSED_BEADS_SERVER_SOCKET— exported by the launcher with the agent-side socket path. -
Sync:
harnessed svc sync beads-serverexecsbd dolt pushinside the beads-server container to pushrefs/dolt/datato the git remote.bdmust be baked into the service image (not the agent image) becausedolt sql-serverholds an exclusive lock on the data directory.
Each supported harness has its own credential mechanism.
| Harness | Auth mechanism | Mount strategy |
|---|---|---|
claude |
Primary: CLAUDE_CODE_OAUTH_TOKEN, forwarded as a bare -e from the host env or an env-file (_claude_oauth_token_args, launcher.py:2073). Legacy fallback: ~/.claude/.credentials.json, used only when no token is configured |
With a token, no credential file is mounted at all. Without one, _claude_creds_seed_mount (launcher.py:2119) seeds a per-instance copy under $XDG_STATE_HOME/harnessed/<inst>/ and mounts it rw, re-seeding when the copy has expired. Its own docstring calls this an acknowledged anti-pattern |
omp |
~/.omp/agent/ dir (auth + usage + sessions together, no separately-mountable credential file) |
Host dir bind-mounted read-write; --profile is never used (it creates an isolated empty store) |
opencode |
Config baked into harness image; project dir bind-mounted | Per catalog/base/Dockerfile.harnessed-opencode
|
codex |
Config baked; MCP pointed at hatago hub | Per catalog/base/Dockerfile.harnessed-codex
|
antigravity |
agy binary + baked config |
Per catalog/base/Dockerfile.harnessed-antigravity
|
CLAUDE_CONFIG_DIR (claude) and PI_CODING_AGENT_DIR (omp) move config and credentials together on the host backend, so the per-stack host home holds skill/rule/command content while sessions/, memories/, and history are symlinked back to the shared store.
Three workflows in .github/workflows/:
| Workflow | Trigger | What it does |
|---|---|---|
test.yml |
PR + push to main
|
Runs hermetic pytest suite via uv run --extra dev pytest. No podman on runner; HARNESSED_PODMAN-gated integration tests skip. Python 3.12 via astral-sh/setup-uv@v7. |
pin-check.yml |
Scheduled (Mondays 06:00 UTC) + workflow_dispatch
|
harnessed update --check over the catalog. Resolves live npm/PyPI/GitHub registries. Not a PR gate (a third-party release would fail unrelated PRs). Installs mise via jdx/mise-action@v2 for aqua-backend resolution. |
deploy-web.yml |
(separate trigger) | Deploys the web/ directory. |
-
What: An enterprise SSL-intercepting proxy CA certificate that must be trusted inside the container so
curl/pip/pnpm/etc. downloads succeed. -
How injected: Detected by
_corp_proxy_ca_secret_args()inlauncher.py. Passed topodman buildas--secret id=corp_proxy_ca,src=<cert>— theDockerfile.harnessed-basereceives it viaRUN --mount=type=secret,id=corp_proxy_ca,...and adds it to the system trust store without baking the cert into a layer. -
Service Dockerfiles: A standard CA block is injected into service Dockerfiles automatically (see
CA_BLOCKinlauncher.py).
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)