# syntax=docker/dockerfile:1 # dockerfiles/Dockerfile.hermes-webui # # Both FROM bases below are multi-arch (linux/amd64 + linux/arm64). Kaniko builds # one native leaf per node arch (arm64 on titan-20, amd64 on titan-24) and each # leaf selects the matching arch from these indexes; ci/scripts/hermes_multiarch_combine.py # then binds the two leaves into one manifest list. This is what lets the agent # pod's `hux` sidecar (which runs this image) schedule onto the amd64 node titan-22. # # The upstream WebUI base is a multi-arch OCI index # (sha256:a83a3893... -> amd64 sha256:54fd4990..., arm64 sha256:9094ae6a...). It is # mirrored digest-for-digest into the in-cluster Harbor `mirror` project by # services/harbor/hermes-webui-base-mirror-job.yaml so the build never depends on # ghcr.io egress (flaky from build pods). Bump this digest and BOTH args in that # Job together, then an operator re-runs the (suspended) Job once. FROM harbor-core.harbor.svc.cluster.local/mirror/hermes-webui@sha256:a83a3893111dcb250e7aa7aa657d3d6f4570b0e2fd00d9b7569246fc5e7339b2 AS webui # Layer each arch's WebUI on the matching leaf of the multi-arch Hermes agent index # (sha256:a68d1c4d... -> amd64 sha256:c89ac4bc..., arm64 sha256:572854cb...), a Docker # manifest list already in Harbor's bstein project. Kaniko's --registry-mirror pulls # it internally through harbor-core. FROM registry.bstein.dev/bstein/hermes-agent@sha256:a68d1c4d5517cc5e6719661e77be4f18d4964b07a85dcebaf6f62368646e4e6c ARG HERMES_WEBUI_RELEASE_ID USER root # Keep WebUI and Hermes pinned together. The WebUI imports Hermes internals, # while the gateway remains the only process that owns an agent conversation. COPY --from=webui /apptoo /opt/hermes-webui COPY dockerfiles/hermes-hux-foundation/hux /opt/hermes-hux/hux COPY dockerfiles/hermes-hux-foundation/hux_producer /opt/hermes-hux/hux_producer COPY services/hermes/contracts/hux /opt/hermes-hux/contracts ENV HUX_CONTRACT_DIR=/opt/hermes-hux/contracts # Add the Atlas voice bridge as a narrow integration layer. It activates only # when a tenant's server-side STT capability reports the private Jetson route. COPY dockerfiles/hermes-webui-base-patch.py /tmp/hermes-webui-base-patch.py COPY dockerfiles/hermes-webui-atlas-patch.py /tmp/hermes-webui-atlas-patch.py COPY dockerfiles/hermes-webui-stt-patch.py /tmp/hermes-webui-stt-patch.py COPY dockerfiles/hermes-webui-telegram-project-patch.py /tmp/hermes-webui-telegram-project-patch.py COPY dockerfiles/hermes-webui-atlas-voice.js /opt/hermes-webui/static/atlas-voice.js COPY dockerfiles/hermes-webui-atlas-voice-worklet.js /opt/hermes-webui/static/atlas-voice-worklet.js COPY dockerfiles/hermes-webui-atlas-voice.css /opt/hermes-webui/static/atlas-voice.css COPY dockerfiles/hermes-webui-router-patch.py /tmp/hermes-webui-router-patch.py COPY dockerfiles/hermes-webui-router.js /opt/hermes-webui/static/atlas-router.js COPY dockerfiles/hermes-webui-hux-bff-patch.py /tmp/hermes-webui-hux-bff-patch.py COPY dockerfiles/hermes-webui-hux-context.py /tmp/hermes-webui-hux-context.py COPY dockerfiles/hermes-webui-hux-context-patch.py /tmp/hermes-webui-hux-context-patch.py COPY dockerfiles/hermes-webui-hux-patch.py /tmp/hermes-webui-hux-patch.py COPY dockerfiles/hermes-webui-hux/foundation.css /opt/hermes-webui/static/hux/foundation.css COPY dockerfiles/hermes-webui-hux/foundation.js /opt/hermes-webui/static/hux/foundation.js COPY dockerfiles/hermes-webui-hux/shell.js /opt/hermes-webui/static/hux/shell.js COPY dockerfiles/hermes-webui-hux/runtime/autonomy-privacy.css /opt/hermes-webui/static/hux/runtime/autonomy-privacy.css COPY dockerfiles/hermes-webui-hux/runtime/autonomy-privacy.js /opt/hermes-webui/static/hux/runtime/autonomy-privacy.js COPY dockerfiles/hermes-webui-hux/runtime/wave_a_activity_memory.css /opt/hermes-webui/static/hux/runtime/wave_a_activity_memory.css COPY dockerfiles/hermes-webui-hux/runtime/wave_a_activity_memory.js /opt/hermes-webui/static/hux/runtime/wave_a_activity_memory.js COPY dockerfiles/hermes-webui-hux/runtime/wave_a_contract.js /opt/hermes-webui/static/hux/runtime/wave_a_contract.js COPY dockerfiles/hermes-webui-hux/runtime/wave_b.css /opt/hermes-webui/static/hux/runtime/wave_b.css COPY dockerfiles/hermes-webui-hux/runtime/wave_b_artifacts_research.js /opt/hermes-webui/static/hux/runtime/wave_b_artifacts_research.js COPY dockerfiles/hermes-webui-hux/runtime/wave_b_contract.js /opt/hermes-webui/static/hux/runtime/wave_b_contract.js COPY dockerfiles/hermes-webui-hux/runtime/wave_b_projects_modes.js /opt/hermes-webui/static/hux/runtime/wave_b_projects_modes.js COPY dockerfiles/hermes-webui-hux/runtime/wave_b_runtime.js /opt/hermes-webui/static/hux/runtime/wave_b_runtime.js COPY dockerfiles/hermes-webui-hux/runtime/wave_c_multimodal_onboarding_release.css /opt/hermes-webui/static/hux/runtime/wave_c_multimodal_onboarding_release.css COPY dockerfiles/hermes-webui-hux/runtime/wave_c_multimodal_onboarding_release.js /opt/hermes-webui/static/hux/runtime/wave_c_multimodal_onboarding_release.js COPY dockerfiles/hermes-webui-hux/bootstrap.css /opt/hermes-webui/static/hux/bootstrap.css COPY dockerfiles/hermes-webui-hux/bootstrap.js /opt/hermes-webui/static/hux/bootstrap.js COPY dockerfiles/hermes-webui-brand-patch.py /tmp/hermes-webui-brand-patch.py COPY dockerfiles/hermes-webui-manifest-patch.py /tmp/hermes-webui-manifest-patch.py COPY dockerfiles/hermes-webui-release-patch.py /tmp/hermes-webui-release-patch.py COPY dockerfiles/hermes-webui-smoke.py /tmp/hermes-webui-smoke.py COPY dockerfiles/hermes-webui-brand.css /opt/hermes-webui/static/hermes-brand.css COPY dockerfiles/hermes-webui-manifest.json /tmp/hermes-webui-manifest.json COPY dockerfiles/hermes-webui-assets/hermes-agent.ico /opt/hermes-webui/static/hermes-agent.ico COPY dockerfiles/hermes-webui-assets/hermes-agent-192.png /opt/hermes-webui/static/hermes-agent-192.png COPY dockerfiles/hermes-webui-assets/hermes-agent-512.png /opt/hermes-webui/static/hermes-agent-512.png RUN /opt/hermes/.venv/bin/python /tmp/hermes-webui-base-patch.py \ && /opt/hermes/.venv/bin/python /tmp/hermes-webui-atlas-patch.py \ && /opt/hermes/.venv/bin/python /tmp/hermes-webui-stt-patch.py \ && /opt/hermes/.venv/bin/python /tmp/hermes-webui-telegram-project-patch.py \ && /opt/hermes/.venv/bin/python /tmp/hermes-webui-router-patch.py \ && /opt/hermes/.venv/bin/python /tmp/hermes-webui-hux-bff-patch.py \ && /opt/hermes/.venv/bin/python /tmp/hermes-webui-hux-context-patch.py \ && /opt/hermes/.venv/bin/python /tmp/hermes-webui-brand-patch.py \ && /opt/hermes/.venv/bin/python /tmp/hermes-webui-manifest-patch.py \ && /opt/hermes/.venv/bin/python /tmp/hermes-webui-hux-patch.py \ && HERMES_WEBUI_RELEASE_ID="${HERMES_WEBUI_RELEASE_ID}" \ /opt/hermes/.venv/bin/python /tmp/hermes-webui-release-patch.py RUN /opt/hermes/.venv/bin/python -c 'import cryptography, yaml' \ && test -f /opt/hermes-hux/contracts/flags.json \ && test -f /opt/hermes-hux/contracts/release-ledger.schema.json \ && PYTHONPATH=/opt/hermes-hux /opt/hermes/.venv/bin/python -m compileall -q /opt/hermes-hux/hux /opt/hermes-hux/hux_producer \ && PYTHONPATH=/opt/hermes-hux /opt/hermes/.venv/bin/python -c 'from pathlib import Path; from tempfile import TemporaryDirectory; from hux.server import build_router; root = TemporaryDirectory(); router = build_router(Path(root.name), {"HUX_FLAGS": ""}); assert router.routes; root.cleanup()' \ && grep -Fq 'VALID_REASONING_EFFORTS = ("minimal", "low", "medium", "high", "xhigh")' \ /opt/hermes-webui/api/config.py \ && ! grep -Fq 'data-effort="max"' /opt/hermes-webui/static/index.html \ && grep -Fq 'res.status===401||res.status===403' /opt/hermes-webui/static/ui.js \ && grep -Fq "window.location.assign('/oauth2/start?rd='" /opt/hermes-webui/static/ui.js \ && grep -Fq "childrenExpanded?'▾ ':'▸ '" /opt/hermes-webui/static/sessions.js \ && grep -Fq "TELEGRAM_PROJECT_NAME = 'Telegram'" /opt/hermes-webui/api/models.py \ && grep -Fq "'atlas/auto/maximum': 'Automatic · Maximum'" /opt/hermes-webui/static/panels.js \ && grep -Fq 'Atlas Jetson (private)' /opt/hermes-webui/static/index.html \ && grep -Fq 'user-scalable=no, viewport-fit=cover' /opt/hermes-webui/static/index.html \ && grep -Fq 'HERMES_WEBUI_ATLAS_TTS_URL' /opt/hermes-webui/api/routes.py \ && grep -Fq 'settings["webui_bundle_version"]' /opt/hermes-webui/api/routes.py \ && grep -Fq 'settings.webui_bundle_version||settings.webui_version' /opt/hermes-webui/static/panels.js \ && grep -Fq 'Audio conversion failed: upload is invalid' /opt/hermes/tools/transcription_tools.py \ && grep -Fq "capability.provider!=='local_command'" /opt/hermes-webui/static/atlas-voice.js \ && grep -Fq 'prefers-reduced-motion: reduce' /opt/hermes-webui/static/atlas-voice.css \ && grep -Fq 'id="voiceInstrumentStyles"' /opt/hermes-webui/static/index.html \ && grep -Fq 'data-priority="maximum"' /opt/hermes-webui/static/index.html \ && grep -Fq 'routing_priority:priority' /opt/hermes-webui/static/atlas-router.js \ && grep -Fq "'atlas/auto/fast':'Automatic · Fast'" /opt/hermes-webui/static/atlas-router.js \ && grep -Fq 'explicit_reasoning_effort' /opt/hermes-webui/api/gateway_chat.py \ && grep -Fq 'from api.hux_bff import proxy_hux' /opt/hermes-webui/api/routes.py \ && grep -Fq 'from api.hux_context import attach_hux_context' /opt/hermes-webui/api/routes.py \ && grep -Fq '"schema": "hux.webui_context.v1"' /opt/hermes-webui/api/hux_context.py \ && grep -Fq '"project_source": raw_project' /opt/hermes-webui/api/hux_context.py \ && grep -Fq 'static/hux/bootstrap.js' /opt/hermes-webui/static/index.html \ && grep -Fq "'./static/hux/bootstrap.js' + VQ" /opt/hermes-webui/static/sw.js \ && grep -Fq 'value.hux_context' /opt/hermes-webui/static/hux/bootstrap.js \ && grep -Fq "'/hux/v1/capabilities'" /opt/hermes-webui/static/hux/bootstrap.js \ && grep -Fq "'/hux/v1/context/bootstrap'" /opt/hermes-webui/static/hux/bootstrap.js \ && grep -Fq 'HermesHuxBootstrap.mergeTrustedContext(S.session, startData)' /opt/hermes-webui/static/messages.js \ && grep -Fq 'respBody.cancelled===true && respBody.stream_id===streamId' /opt/hermes-webui/static/boot.js \ && grep -Fq 'prefers-reduced-motion: no-preference' /opt/hermes-webui/static/hux/bootstrap.css \ && grep -Fq '"language": detected_language' /opt/hermes/tools/transcription_tools.py \ && grep -Fq "'language': detected" /opt/hermes-webui/api/upload.py \ && grep -Fq 'def _atlas_tts_language(body):' /opt/hermes-webui/api/routes.py \ && grep -Fq 'request_payload["language"] = _atlas_language' /opt/hermes-webui/api/routes.py \ && grep -Fq 'takeSttLanguage(token)' /opt/hermes-webui/static/atlas-voice.js \ && grep -Fq "registerProcessor('atlas-pcm-playback'" /opt/hermes-webui/static/atlas-voice-worklet.js \ && grep -Fq "const TTS_STREAM_URL='/api/tts/stream'" /opt/hermes-webui/static/atlas-voice.js \ && grep -Fq "const STT_STREAM_PATH='/api/transcribe/stream'" /opt/hermes-webui/static/atlas-voice.js \ && grep -Fq '