diff --git a/dockerfiles/hermes-webui-hux/NOTES.md b/dockerfiles/hermes-webui-hux/NOTES.md new file mode 100644 index 00000000..c67373e4 --- /dev/null +++ b/dockerfiles/hermes-webui-hux/NOTES.md @@ -0,0 +1,68 @@ +# HUX client foundation integration contract + +The WebUI image packages these files. Its server derives a matching +`hux.webui_context.v1` only from the authenticated, persisted WebUI session, +the bounded server `HUX_PROJECT_SOURCE`, the trusted tenant slot, and the +router-asserted HUX subject. The browser bridge remains inert unless that +context is present and the same-origin capability endpoint advertises a +complete route set. It is versioned as `hux.v1`, contains no tenant-global +browser storage, and defaults to a hidden shell. + +The router negotiation endpoint is `GET /hux/v1/capabilities`. A disabled +foundation returns 404. An enabled response is: + +```json +{ + "schema": "hux.capabilities.v1", + "contract_version": "1.1.0", + "identity": { + "tenant_slot": "slot-3", + "subject": "usr_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + "surface": "chat", + "trust": "relay" + }, + "cards": [ + { + "card": "HUX-11", + "flag": "hux.foundation", + "enabled": true, + "routes": ["/hux/v1/capabilities", "/hux/v1/context/bootstrap"] + } + ], + "server": {} +} +``` + +The boot bridge reads opaque identity and scope only from `S.session.hux_context`. +That record must bind its `webui_session_id` to the loaded WebUI session. The +bridge compares it to the authenticated BFF capability identity before it +mounts anything. It must never pass a raw Keycloak subject, email, Telegram +identifier, or tenant ordinal. The client rejects an identity or surface +mismatch, unknown versions, cross-user objects, and cross-conversation +records. It does not use local/session storage. + +Opaque IDs use a server-owned 32-byte key at `HUX_CONTEXT_KEY_FILE`, or +`$HERMES_WEBUI_STATE_DIR/.hux-context-key` by default. The file is created +before context is emitted and must remain an owner-only regular file. Chat and +Worker derive the same IDs with `hux.context.id.v1`; no browser value can +select the subject, slot, key, or default project source. The project source +defaults to `profile:default` when its server environment override is absent. + +After capability negotiation, the bridge sends the exact server-derived scope +to `POST /hux/v1/context/bootstrap` with CSRF and an idempotency key. The HUX +service recomputes every ID from the shared key and creates or verifies the +project/conversation binding. The drawer is not created unless that response +matches the authenticated context exactly. + +Feature extensions register an id, label, HUX flag, display order, and render +callback with `HermesHuxShell.createShell()`. The shell is hidden for a 404, +for an off foundation, or when no registered extension is enabled. Loading and +errors are accessible status regions; errors disclose no backend body. Event +adapters omit `detail`, evidence URIs, hashes, and tool payloads. Object +adapters return display metadata only. Feature code must fetch sensitive +details from its owning tenant-scoped endpoint after an explicit user action. + +Scripts load in dependency order and the image service worker caches the same +reviewed assets. No manifest or live HUX flag is changed by this browser +bundle. A 404, missing server context, incomplete route advertisement, or +session change destroys or suppresses the workspace.