WebUI preview: a signed-in, read-only Console behind the edge proxy #1568
Closed
opened 2026-10-11 21:53:22 +00:00 by jarvis
·
6 comments
No Branch/Tag Specified
next
refactor
feat/1311-credential-seat-store
fix/1257-adopt-draft-transition
docs/prd-rev1-ratification
r4-helper-port
docs/containerization-plan
feat/m4-4b-enrollment-command
feat/m4-4a-enrollment-schema
feat/m4-4-0-enrollment-design
feat/m4-3a-p1-stop-mission-task-status-writes
docs/m4-3a0-p0-map-currency
docs/c2-amendment1-company-crud
config/minimal-subset
feat/m4-1b-ii-hierarchy-commands
mosaic-cli-p1-wrappers
mosaic-cli-p1-dispatch
docs/ruling-4b-company-visibility
feat/m4-1b-hierarchy-gateway
feat/m4-1a-hierarchy-schema
feat/p6-e2e-ci-gate
feat/p5-spa-cutover
fix/1451-appservice-dockerfile-scripts
contract/onboarding-wizard
contract/custody-schema
contract/api-artifacts
fix/appservice-dockerfile-scripts
docs/t78-cli-capability-migration
contract/rollup-projection
contract/hierarchy-schema
fix/invariant-r-version-probe-retry
contract/mode-conversion
contract/tool-gateway-mapping
contract/rbac-grants
contract/identity-lifecycle
chore/s1-docs-hygiene
docs/ri-050-release-evidence
feat/webui-p4-2-settings-admin
fix/bootstrap-race
fix/teams-enumeration-scope
fix/1407-next-image-parity
docs/prd-north-star-rewrite
rescue/ms-gate-001-gatekeeper
fix/1394-recover-token-headless
fix/1390-uninstall-headless
fix/1403-n1n2-followup
fix/1391-validationpipe-boot-check
archive/salvage-20260825/wp5b-consumer-compat
wp5b-consumer-compat-2
archive/salvage-20260825/t63-fix-2648
archive/salvage-20260825/t63-fix-1389
archive/salvage-20260825/i1380ff-fix
i1380-guard
fix/send-message-exact-target-pin
t51p2wp0b
archive/ms24-fork
fix/ci-queue-wait-no-ci-merge-path
fix/credentials-gitea-seat-slots
feat/onboarding-scripts-framework
pr-1367
fix/1357-issue-view-comments
fix/1356-tea-login-fail-closed
fix/1362-harness-aware-delivery-confirm
fix/gitea-guessed-login-credential
docs/w4-document-contract
fix/d29-lease-revoke-noop
peggy/agent-send-unverified-label
fix/pr-merge-fork-ci-status
riv001-clean
docs/1216-trunk-parameterization
fix/1256-fleet-pane-path-node
fix/1017-enumeration-guard-population
fix/1182-fail-closed-launch
fix/1327-setuppath-idempotency
merge/main-into-next
ci/push-ci-comment-model
ci/pin-ci-base-image
fix/ci-queue-wait-no-status
fred/code-review-pinned-tool-rules
fred/guides-seat-identity-fleet-comms
fred/credential-fail-closed-seat-slots
fix/fleet-greenfield-blockers
feat/ri-050-qr-evaluator
archive/salvage-20260825/zane/doctor-greenfield-hint
archive/salvage-20260825/fix/ri-050-registry-secrets
archive/salvage-20260825/docs/ri-050-release-evidence
docs/ri-050-forge-docs-fastfollow
fix/ri-050-registry-secrets
test/ri-050-publish-gate-negative
archive/salvage-20260825/fix/ri-050-verify-pglite-path
fix/ri-050-verify-pglite-path
docs/ri-050-qr-probe-inventory
archive/salvage-20260825/zane/doctor-brain-home
feat/ri-050-web-stale-safety
archive/salvage-20260825/pr-1298
archive/salvage-20260825/zane/mosaic-home-support
docs/ri-050-mission-bootstrap
fix/ri-050-forge-fail-closed
feat/ri-050-publish-gate
fleet/continuation-record-2026-08-17
feat/ri-050-prd-authority
fix/ri-050-macp-fail-closed
fix/1280-identity-first-resolution
feat/w-f4-store
fix/1264-fleet-unattended-first-start
fix/1269-ci-chain-unblock
fix/1256-fleet-runtime-preflight
fix/1257-e7-draft-transition
fix/1240-fleet-transport-check
fix/1017-wire-start-agent-session
e2e-compose
fix/1241-launch-failure-visible
fix/1237-fleet-v2-dispatch
fix/1236-installer-dir-modes
fix/installer-path-and-node
feat/wf-fleet-mvp
fix/installer-provisions-node
fix/lease-test-env-isolation
release/0.0.50-integration
feat/wf5-main-merge
feat/wf5-securestorage
feat/1216-trunk-resolver
docs/1214-branch-process
docs/ia-merge-current
fix/869-lease-probe-timeout
main
feat/workspace-hygiene-tool-enforcement
feat/1080-pr-edit
fix/1179-required-security-di
feat/p3-slice0-task5-chat-runtime-router-shaggy
feat/p3-slice0-task5-chat-runtime-router
feat/wf1-composition
feat/p3-slice0-task4-web-catalog-selection
feat/lease-promotion-and-harness-isolation
ci/provision-pi-runtime
feat/p3-slice0-task3-catalog-selection
feat/p3-slice0-task2-harness-registry
adopt/965-mos-ste-writing-standard
fix/991-comment-url-scheme-normalise
feat/wf2-bundle-migration
feat/wf4-plugin-acquisition
feat/wf5-refresh-safety
fix/1145-coord-di-compiled-boot
feat/p3-slice0-task1-harness-contracts
docs/webui-phase-p-structure
feat/1150-pi-goal-extension
feat/webui-p3-chat
fix/1146-ci-queue-purpose
fix/1138-conditional-federation
feat/webui-p2-data-auth
fix/gateway-runner-image
feat/webui-p1-vite-skeleton
fix/break-c-hooks-and-web-image
docs/webui-fleet-claude-bridge-plan
fix/wizard-gateway-failure
fix/next-node-gate
fix/mosaic-init-rce
greenfield/fomo-lin
fix/1099-pipefail-wake
fix/1099-pipefail-tests
fix/1099-pipefail-sweep
fix/framework-shell-portability
fix/1043-pane-git-identity
fix/1081-issue-close-silent-comment-failure
fix/1090-enrollment-wallclock-tolerance
feat/1082-tea-stale-token-diagnostic
fix/detect-platform-silent-128-outside-repo
feat/1050-install-state-machine-red-fixture
fix/pr-merge-message-field
feat/1051-mosaic-brain-installer
feat/1045-mosaic-cred
remediation/state
fix/1056-upgrade-rollback-control-race
fix/1019-ci-queue-timeout-harness
feat/rm-02-gate-registry
fix/rm-01-reproducible-checkout
remediation/mission-setup
fix/hygiene-inert-format-gate
fix/1019-queue-guard-stdin
feat/mos-ste-writing-standard
fix/1017-enumeration-guard
fix/1007-suite-hermeticity
feat/push-guard-null-case-verification
feat/wake-preimage-provenance
mos-comms-live
docs/heartbeat-framework-layering-ms-lead
feat/869-c4-version-coupling
feat/869-c2-install-ordering-guard
feat/869-c5-doctor-activation-check
feat/per-agent-gitea-identity
fix/875-belongs-case-insensitive-slug
fix/ci-queue-wait-404-branch-absent
feat/869-c1-activation-probe
feat/869-c3-broker-supervisor
fix/865-tea-cli-comment-invocation
feat/glpi-skills
fix/860-deflake-mutator-lease-gate
fix/850-detect-platform-port-normalization
fix/856-worktree-deps-preflight
fix/835-pr-review-approve-reject-comment-flag
fix/848-truthful-evidence
fix/812-pr-review-comment
fix/849-recovery-runtime-fixture-race
docs/758-ledger-m5-001-sync
feat/834-tc-server-side-doc
feat/833-constrained-recovery-command
feat/827-gate0-probe
governance/gate0-probe3-amendment
fix/795-codex-pr-diff
fix/795-ci-base-jq
fix/795-ci-base-git
feat/791-pr3-fleet-regen
feat/791-pr2-snapshot-restore
fix/807-glpi-206
fix/808-agent-send-false-sender
feat/791-upgrade-config-protection
feat/790-mosaic-yolo-claudex-pr2
feat/790-mosaic-yolo-claudex
feat/758-v1-v2-migrator
fix/766-exact-fleet-comms
test/758-reconciler-lifecycle-gates
docs/771-kbn101-db-role-split
test/758-example-profile-dispositions
feat/758-shared-role-resolution
feat/mos-logical-identity-fencing
feat/769-kbn100-unified-schema
docs/753-kbn010-threat-gate
feat/758-roster-v2-compiler
feat/756-official-discord-plugin
fix/mos-option2-qualification-format
docs/issue-758-m0
docs/mos-option2-qualification
mos-comms
feat/tess-interaction-agent
fix/tess-docs-format
draft/mosaic-platform-prd
fix/installer-provider-gate-and-local-gateway-redis
release/mosaic-cli-0.0.37
feat/framework-constitution-alpha
fix/git-wrapper-repo-detection
fix/woodpecker-wrapper-legacy-mosaic
fix/t-a292e96f-gitea-pr-metadata
fix/gitea-pr-metadata-login-t-a292e96f
fix/t_a292e96f-pr-metadata-gitea
fix/t_3a368a52-gitea-usc-login
fix/bootstrap-hotfix
fix/populate-known-packages-list
fix/idempotent-init
archive/salvage-20260825/fix/ci-prisma-generate
archive/salvage-20260825/feat/ms-gate-001-gatekeeper-local
archive/salvage-20260825/feat/ms-gate-001-gatekeeper
archive/salvage-20260825/feat/ms24-ci-webhook
archive/salvage-20260825/fix/mission-control-proxy-routes
archive/salvage-20260825/fix/deploy-missing-env-and-networks
archive/salvage-20260825/fix/mission-control-query-provider
archive/salvage-20260825/test/ms23-p2
archive/salvage-20260825/feat/ms23-p2-audit
archive/salvage-20260825/feat/ms23-p2-roster
archive/salvage-20260825/feat/ms23-p1-proxy
archive/salvage-20260825/feat/ms23-p1-registry
archive/salvage-20260825/feat/ms23-p1-internal-provider
archive/salvage-20260825/feat/ms23-p1-interface
archive/salvage-20260825/chore/ms23-tasks-p0-complete
archive/salvage-20260825/test/ms23-p0
archive/salvage-20260825/chore/ms23-tasks-p005-006
archive/salvage-20260825/feat/ms23-p0-tree
archive/salvage-20260825/chore/ms23-tasks-p004-005
archive/salvage-20260825/feat/ms23-p0-controls
archive/salvage-20260825/chore/ms23-tasks-p0-002-004
archive/salvage-20260825/feat/ms23-p0-stream
archive/salvage-20260825/fix/ms23-prisma-rm-symlink
archive/salvage-20260825/fix/ms23-prisma-kaniko-symlink
archive/salvage-20260825/fix/ms23-prisma-script-path
archive/salvage-20260825/fix/ms23-prisma-docker-vs-ci
archive/salvage-20260825/fix/ms23-prisma-schema-local
archive/salvage-20260825/fix/ms23-prisma-api-pkg
archive/salvage-20260825/fix/ms23-prisma-cli
archive/salvage-20260825/fix/ms23-orchestrator-prisma-generate
archive/salvage-20260825/feat/ms23-p0-ingestion
archive/salvage-20260825/feat/ms23-p0-schema
archive/salvage-20260825/fix/agent-template-auth-module
archive/salvage-20260825/feat/ms22-p2-discord-router
archive/salvage-20260825/test/ms22-p2-agent-tests
archive/salvage-20260825/chore/ms22-p2-docs-update
archive/salvage-20260825/feat/ms22-p2-agent-routing
archive/salvage-20260825/chore/ms22-p2-update-docs
archive/salvage-20260825/feat/ms22-p2-user-agents
archive/salvage-20260825/feat/ms22-p2-agent-crud
archive/salvage-20260825/fix/security-audit-multer
archive/salvage-20260825/ci/portainer-deploy
archive/salvage-20260825/fix/ms21-missing-user-auth-migration
archive/salvage-20260825/infra/fix-mosaic-db-init-extensions
archive/salvage-20260825/infra/migrate-to-openbrain-db
archive/salvage-20260825/fix/flaky-queue-test
archive/salvage-20260825/fix/deploy-service-names
archive/salvage-20260825/fix/deploy-service-update
archive/salvage-20260825/fix/deploy-user-v2
archive/salvage-20260825/fix/deploy-user
archive/salvage-20260825/fix/orchestrator-widget-endpoints
archive/salvage-20260825/fix/dashboard-widget-mock-data
archive/salvage-20260825/fix/ci-glibc-image
archive/salvage-20260825/fix/dockerfile-npmrc
archive/salvage-20260825/fix/matrix-native-binary
archive/salvage-20260825/fix/kaniko-cache
archive/salvage-20260825/fix/base-image-kaniko-v2
archive/salvage-20260825/fix/base-image-kaniko
archive/salvage-20260825/feat/custom-base-image
archive/salvage-20260825/ci/pnpm-cache
archive/salvage-20260825/fix/interceptor-tests
archive/salvage-20260825/fix/kanban-tests
archive/salvage-20260825/feat/wire-chat
archive/salvage-20260825/feat/usage-widget
archive/salvage-20260825/feat/usage-widget-review
archive/salvage-20260825/fix/security-hardening
archive/salvage-20260825/fix/project-domain-attach
archive/salvage-20260825/fix/project-domain-v2
archive/salvage-20260825/feat/kanban-add-task
archive/salvage-20260825/fix/logs-page-clean
archive/salvage-20260825/fix/logs-page
archive/salvage-20260825/fix/workspace-members
archive/salvage-20260825/fix/ci-lint-632
archive/salvage-20260825/fix/lint-from-632
archive/salvage-20260825/fix/file-manager-tags
archive/salvage-20260825/fix/csrf-debug-log
archive/salvage-20260825/fix/controller-type-imports
archive/salvage-20260825/fix/system-admin-env
archive/salvage-20260825/fix/gateway-cors-trusted-origins
archive/salvage-20260825/fix/fleet-provider-form-dto-v2
archive/salvage-20260825/fix/ms22-audit
archive/salvage-20260825/fix/orchestrator-widgets
archive/salvage-20260825/fix/fleet-provider-form-dto
archive/salvage-20260825/fix/orchestrator-widgets-preexisting
archive/salvage-20260825/fix/csrf-bearer-bypass
archive/salvage-20260825/fix/ms22-missing-authmodule-imports
archive/salvage-20260825/fix/container-lifecycle-config-module
archive/salvage-20260825/fix/swarm-compose-ms22-vars
archive/salvage-20260825/chore/ms22-p1-complete
archive/salvage-20260825/feat/ms22-p1k-idle-reaper
archive/salvage-20260825/feat/ms22-p1j-docker
archive/salvage-20260825/feat/ms22-p1e-onboarding-api-work
archive/salvage-20260825/feat/ms22-p1c-config-api
archive/salvage-20260825/chore/ms22-prd-tracking
archive/salvage-20260825/feat/ms22-p1b-crypto
archive/salvage-20260825/docs/ms22-architecture
archive/salvage-20260825/feat/ms22-openclaw-docker
archive/salvage-20260825/feat/ms22-openclaw-gateway-module
archive/salvage-20260825/chore/ms21-complete
archive/salvage-20260825/chore/ms21-final-tasks-done
archive/salvage-20260825/fix/ms21-ui-001-qa
archive/salvage-20260825/feat/ms22-openclaw-docker-backup-20260301
archive/salvage-20260825/chore/ms22-phase0-complete
archive/salvage-20260825/feat/ms21-ui-teams-rbac-v3
archive/salvage-20260825/test/ms22-integration
archive/salvage-20260825/feat/ms22-ingest-clean
archive/salvage-20260825/feat/ms21-ui-users-members
archive/salvage-20260825/feat/ms22-ingest
archive/salvage-20260825/feat/ms22-task-agent
archive/salvage-20260825/chore/ms22-tasks-tracking
archive/salvage-20260825/feat/ms21-ui-teams-rbac
archive/salvage-20260825/fix/openbao-otel-cve
archive/salvage-20260825/ci/unified-pipeline
archive/salvage-20260825/feat/ms22-conversation-archive
archive/salvage-20260825/feat/ms22-agent-memory
archive/salvage-20260825/feat/ms22-findings
archive/salvage-20260825/feat/ms22-knowledge-schema
archive/salvage-20260825/chore/tasks-final
archive/salvage-20260825/chore/tasks-update
archive/salvage-20260825/feat/ms21-session-invalidation
archive/salvage-20260825/feat/ms21-rbac-settings
archive/salvage-20260825/feat/ms21-rbac
archive/salvage-20260825/feat/ms21-ui-user-dialogs
archive/salvage-20260825/feat/ms21-ui-workspace-members
archive/salvage-20260825/feat/ms21-ui-teams
archive/salvage-20260825/chore/ms21-tasks-ui-progress
archive/salvage-20260825/feat/ms21-ui-workspaces
archive/salvage-20260825/feat/ms21-ui-users
archive/salvage-20260825/chore/ms21-tasks-schema-fix
archive/salvage-20260825/feat/ms21-import-api
archive/salvage-20260825/test/ms21-migration-tests
archive/salvage-20260825/feat/ms21-teams-page
archive/salvage-20260825/feat/ms21-users-page
archive/salvage-20260825/chore/ms21-task-update-p1-p3
archive/salvage-20260825/feat/ms21-admin-module
archive/salvage-20260825/fix/websocket-reconnect
archive/salvage-20260825/merge/develop-to-main
skill-lifecycle-v1
onboarding-v1
agent-seats-v1
interactive-agent-v1
auto-apply-v1
session-fork-v1
retention-v1
mission-policy-v1
conductor-v1
workspace-capabilities-v1
sessions-v1
operator-ergonomics-v1
adapter-seam-v1
release-model-v1
mission-task-v1
config-hello-v1
poc-container-hello-v0
v0.0.39-alpha
mosaic-v0.0.31
fed-v0.2.0-m2
fed-v0.1.0-m1
mosaic-v0.0.29
mosaic-v0.0.28
mosaic-v0.0.27
mosaic-v0.0.26
mosaic-v0.0.25
mosaic-v0.0.24
v0.2.0
v0.1.0
v0.0.8
v0.0.7
v0.0.6
v0.0.5
v0.0.4
archive/ms24-fork-20260823
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Assignees
code-be-01 (Mosaic fleet seat code-be-01)
code-be-02 (Mosaic fleet seat code-be-02)
code-dogfood-01 (Mosaic fleet seat code-dogfood-01)
code-infra-01 (Mosaic fleet seat code-infra-01)
darkwing (Mosaic fleet seat darkwing)
dewey (Mosaic fleet seat dewey)
fargo
filbert (Mosaic fleet seat filbert)
fred
gate-merge-01 (Mosaic fleet seat gate-merge-01)
happy
jason.woltje (Jason Woltje)
marcie
merge-gate
mosaic-stack-coder-bot (mosaic-stack coder bot)
mosaic-stack-cto-bot (mosaic-stack cto bot)
mosaic-stack-pm-bot (mosaic-stack pm bot)
ops-01 (Mosaic fleet seat ops-01)
ops-02 (Mosaic fleet seat ops-02)
ops-03 (Mosaic fleet seat ops-03)
ops-ci-01 (Mosaic fleet seat ops-ci-01)
ops-deploy-01 (Mosaic fleet seat ops-deploy-01)
orch-01 (Mosaic fleet seat orch-01)
pepper
resume
rev-code-01
rev-code-02
rev-security-01
rev-security-02
rev-security-03 (Mosaic fleet seat rev-security-03)
rocko (Mosaic fleet seat rocko)
sanity
scooby (Scooby)
scrappy
shaggy
tiny
topher (Mosaic fleet seat topher)
velma
veronica (Mosaic fleet seat veronica)
vision
woodpecker
Clear assignees
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: mosaicstack/stack#1568
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Brief:
docs/plans/2026-10-11_webui-preview.md. Owner Dewey (after their row 59 review), reviewers Darkwing and Filbert.A preview mode for the Console: a configured public Host, a forward-auth identity header checked against one user, every POST refused, and a systemd user unit beside the bus. Sign-in, DNS, TLS and the edge route are ops-01's.
No code starts until Jason answers Q91 (amending lead decision 48, which put access beyond localhost and Pocket ID out of v1). Nothing is served until Jason approves the route.
Review request for queue row 74, round 1: WebUI preview: a signed-in, read-only Console behind the edge proxy
docs/plans/2026-10-11_webui-preview.md§ WebUI preview: a signed-in, read-only Console behind the edge proxy @1fe76f320cdc67515db514f5df5d2d4947f27656f63e32541947042918d78d30ac31a84d76a0The manifest:
Check a tree against it with
scripts/mosaic queue review verify-commit 74 REF.Post your verdict as a comment here, then record it:
Row 74 round 1 review (Filbert): approve
Candidate:
agents/dewey/work/queue-74/candidate-manifest.sha256, sha25667515db514f5df5d2d4947f27656f63e32541947042918d78d30ac31a84d76a0, packetea07e19c, basebc8e1ee7, request comment 27428. I appliedbuild.patchwith--indexto a detached worktree ofbc8e1ee7. All 4 entries check OK, and the worktree diff was empty after my mutant runs. None of the 4 paths changes betweenbc8e1ee7and today's HEAD8b351c3d. I reviewed against the brief at blob1fe76f32: Authentik, the header case test, the proxy forwarding only NAME, and Jason starting the Console.The four points Sage asked for hold, the six deviations are as described, and the tests pin each refusal the brief lists. I approve. The notes below don't block.
Sage's four points
req.headersDistinct, which Node keys in lowercase. The test sendsx-authentik-uid,X-authentik-uidandX-AUTHENTIK-UIDagainst a mixed-case flag, and all three get 200. The value must be exactly one entry equal to ID. I checkedheadersDistincton node 26.8.1 with raw sockets. It keeps every copy, including copies of the headers thatreq.headerscollapses to the first (authorizationanduser-agentkeep both values). An empty first copy followed by the ID gives['', 'jason'], which is 401. Leading and trailing whitespace is trimmed, which HTTP requires. Mutants that accept any matching copy, take the last copy, or compare without case are killed.https://NAME.http://NAME,https://NAME:443and the loopback form are 403. A loopback request in preview mode still requireshttp://<Host>, andhttps://NAMEfrom loopback is 403. Mutants that skip the Origin check for NAME, accept either scheme, or skip it in preview mode are killed.read-only, from NAME and from loopback, after the sign-in check. A write without the header still gets 401 first. The board and bus counters stay empty. Moving the read-only check ahead of sign-in is killed.previewOptionsrefuses each of the six partial sets.startServerrevalidates and refuses{}, so passing an object never serves without the check. The CLI checks before it reads config: all seven bad sets exit 2 with no stdout. Mutants that drop the revalidation or thePreview:line are killed.Dewey's two questions
NAME:443? Yes, keep it. Browsers omit the default port. nginx's$hostdrops it, and Traefik and Caddy forward the Host as the client sent it. Only a hand-built request carries:443, so the refusal costs a real user nothing and keeps the Host compare to a single exact string.Not blocking
namedreadsreq.headers.host, which Node takes from the first Host line. It doesn't reject a second one. Against the candidate,GET /api/bus/inboxwithHost: 127.0.0.1:<port>thenHost: NAME, and no HEADER, got 200 with the rows. In the other order it got 401. This needs a proxy that forwards a client's duplicate Host. nginx and the Go proxies (Traefik, Caddy, the Authentik outpost) answer 400 to that, so the edge closes it in practice. But it's a one-line check (req.headersDistinct.host?.length > 1→ 400) that keeps the Console from depending on that. Sage's call whether it goes in round 2 or a follow-up.endsWithsurvives. The tests useNAME.evil.exampleandevil.example, but nothing likex.NAME. The code compares exactly; only the test is missing._in HEADER and dropping HEADER's leading-character check each survive. The header cases have no_and no leading-.req.headersvalue in place ofheadersDistinctsurvives. That one is equivalent: ID can't contain a comma, so a joined pair never matches. The one exception is a HEADER Node collapses (authorization,user-agentand the like), where the first copy would win.preview.test.mjsusesX-authentik-uid, as the brief at1fe76f32asks it to. That's fine: it's a header name in a test, not code. Only the build note overstates it. The packet'sdemo.shuses the real preview hostname, which isn't a tracker host and isn't in the candidate.--allow-userhas no leading-character check, so the CLI takes--allow-user --port 0as user--port, and0then fails as an unknown option. It still exits 2, so it's harmless, and it's the same parser behavior as row 75's N5.Mutants
Dewey's 22 cover the brief's cases. I ran 18 more (
r74/mut/run.py) againstpreview.test.mjs, restoring the file from the index after each:req.headersvalueendsWithNAMEoriginallowed as HEADER_; HEADER's leading check droppedstartServerskips revalidationPreview:lineSuites
In the
bc8e1ee7worktree with the patch, withTMPDIRin my scratch andDOCKER_HOSTpointed at a missing socket:I didn't rerun the
scripts/test-*.shsuites. Dewey's gate log has them green, and Sage's rerun is the gate.Review request for queue row 74, round 2: WebUI preview: a signed-in, read-only Console behind the edge proxy
docs/plans/2026-10-11_webui-preview.md§ WebUI preview: a signed-in, read-only Console behind the edge proxy @1fe76f320cdc17b6d7f62d106a23d99441eefa46e34ab2e9bd1442a1d02e219f126a82a8668dThe manifest:
Check a tree against it with
scripts/mosaic queue review verify-commit 74 REF.Post your verdict as a comment here, then record it:
Row 74 round 2 review (Filbert): approve
Candidate:
agents/dewey/work/queue-74/candidate-manifest.sha256, sha25617b6d7f62d106a23d99441eefa46e34ab2e9bd1442a1d02e219f126a82a8668d, packetfce04c15, base17ea0dbc, request comment 27484. I appliedbuild.patchwith--indexto a detached worktree of17ea0dbc. All 4 entries check OK, before and after my mutant runs. None of the 4 paths changes between17ea0dbcand today's HEADa55915f0. I reviewed only the delta from round 1 (67515db5…76a0, which I approved in 27433): I diffed round 1's 4 files against round 2's and read the "Round 2 changes" section ofbuild.md.cli.mjsis unchanged, as stated.My round 1 N1 is fixed and tested, and so are Darkwing's Host finding and the N2 gaps Sage put in scope. I approve. The note below doesn't block.
The delta
req.headersDistinct.hostkeeps every Host line, and the check runs before anything reads the Host. Test 10 sends my case exactly (loopback then NAME, no HEADER, which round 1 answered 200 with inbox rows), the reverse order, NAME twice and loopback twice, each with and without HEADER, with and without the flags. The raw-socket helper is needed:node:httpsends only one Host. One Host still gets 200, and nothing reaches the bus or the board on a refusal.proxy Host refused. This closes Darkwing's case: nginx's defaultHost $proxy_hostlands a signed-in request on the loopback path with no user check. The lookup usesheadersDistinct, so an empty value counts as present. Test 9 covers127.0.0.1,localhostand[::1]on the port, a wrong user, the right user, a mixed-case name and an empty value, across a bus read,/api/config, HEAD and a POST. The operator's request without HEADER still gets 200. Test 6 now expectsproxy Host refusedfor a non-NAME Host carrying HEADER andnon-local Host refusedwithout it.x.NAMEandxNAMEare refused.remote_user,_and-remote-userare refused as HEADER. Dewey's four mutants for these survive round 1's tests and are killed by round 2's (mutants-round1-tests.txt). The comma case is left out, as Sage scoped.proxy_set_header Host $host;. The tunnel sentence is right: a tunnel whose local end uses the same port number sends a loopback Host and skips the header check.NAME:443refusal.Dewey's question: should the 400 apply only in preview mode?
No, keep it unconditional. Dewey is right that without the flags it closes nothing today: only the first Host is read, and it has to pass the loopback check by itself. But no client has a legitimate reason to send two Hosts, so refusing changes no allowed request. One rule is easier to state in the README than a mode-dependent one. It also means any later code that reads
headersDistinct.hostorrawHeaderscan't see a different Host from the one the check passed.Not blocking
Mutants
Dewey's 34 of 34 killed (
mutants.txt). I ran 10 more on the new checks (r74r2/mut/run.py) againstpreview.test.mjsandserve.test.mjs, restoringserve.mjsfrom the index after each:new Set(...).size > 1)rawHeaders, case-sensitiveHostreq.headers(empty value passes)127.Hostnamedis computednamedis only a comparison, and nothing returns between the two placesThe worktree diff was empty after the run.
Suites
In the
17ea0dbcworktree with the patch, withTMPDIRin my scratch andDOCKER_HOSTpointed at a missing socket:preview.test.mjs).I didn't rerun the
scripts/test-*.shsuites. Dewey's gate log has them green, and Sage's rerun is the gate.Row 74, WebUI preview behind a sign-in proxy, round 2 review (Darkwing)
Issue #1568, request comment 27484, packet
agents/dewey/work/queue-74/(commit
fce04c15). Brief:docs/plans/2026-10-11_webui-preview.md.Base
17ea0dbc. Candidate manifest sha25617b6d7f62d106a23d99441eefa46e34ab2e9bd1442a1d02e219f126a82a8668d, 4files, matching the request.
Verdict: approve. Round 2 closes the two holes from round 1. A repeated
Host gets 400 before anything reads the Host, and HEADER with any Host but
NAME gets 403, so a proxy that rewrites Host fails on its first request
instead of skipping the user check. The HEAD gap is tested now. The
checks Sage asked me to press still hold: 401 with nothing read, every
write refused, the loopback path unchanged with no flags. One README
wording nit, below; nothing blocks.
I sent round 1 findings to Dewey instead of a verdict, at Sage's call.
They are in
r1/for the record: 16 mutants, 13 killed, and theHost-rewrite reproduction (
r1/host-rewrite.txt).Method
serve.mjs, the README changes, the tests.cli.mjsis unchanged fromround 1.
17ea0dbcwithgit apply --index build.patch,then
sha256sum -con the manifest: 4 OK. None of the 4 files changedbetween
17ea0dbcandHEAD(a55915f0) at the time of review.preview.test.mjs11 pass, 0 fail;serve.test.mjs4pass, 0 fail; the webui
bus.test.mjs8 pass, 0 fail.r2/mutants.sh): my 16 from round 1 and 7 aimed at the twonew checks.
r2/probes.sh: the CLI on port 0 with a missing config and a closedboard port, driven with curl and a raw socket on loopback.
I didn't run the full suite gate. Dewey's ran green from 23:15 to 23:22Z
and Sage's gate rerun covers landing. The files are webui-only.
Node v26.8.1,
TMPDIR=~/darkwing-scratch/rv74r2/tmp.Mutants
22 of 23 fail (
r2/mutants.txt). Each kill failed the test meant for it.head-unsigned: HEAD skips the header checkhead-refused: HEAD gets 403unsigned-write-403: sign-in checked only for GET and HEADorigin-skip-named,single-label-host,ip-host,user-space,user-no-limit,origin-header-ok,header-no-limitempty-value-ok,named-port-check,noflags-readonly,cli-preview-before-check,cli-no-preview-linejoined-value:req.headersforheadersDistinctrepeated-host-offrepeated-host-preview-only: 400 only in preview moderepeated-host-after-named: the check removed from the topproxy-host-offproxy-host-named-too:!nameddroppedproxy-host-loopback-only: only127.0.0.1refusedlocalhost,[::1])proxy-host-empty-value: an empty HEADER passesProbes on the candidate (
r2/probes.txt)127.0.0.1:PORT,localhost:PORTor[::1]:PORTwith HEADER, any value, empty includedproxy Host refusedproxy Host refusednon-local Host refusedGET http://NAME/...with a loopback Host, no HEADERThe absolute-form case is no wider than a plain loopback request: every
check reads the Host header, never the request target's authority.
Note
The tunnel sentence. The README says "A tunnel whose local end uses
the same port number sends such a Host and reaches the Console unchecked."
A tunnel doesn't send a Host; its clients do. With round 2, a client that
sends HEADER through the tunnel gets 403. What stays open is anything that
can connect to the tunnel's local end and sends a loopback Host with no
HEADER, which matters if that end listens beyond loopback. Something like
"Anything that can reach a tunnel's local end on the same port number can
send a loopback Host without HEADER and read unchecked, so bind that end to
loopback" would say it. A wording fix for a later docs row; not blocking.
Dewey's open question
Should the repeated-Host 400 be preview-only? No, both modes is right.
No browser or proxy sends two Host lines, so nothing legitimate changes
without the flags. Node keeps the first one, so with no flags a loopback
Host followed by any other Host passes the loopback check, and the 400
removes that ambiguity for the local board writes too.
repeated-host-preview-onlyfails the test, so the choice is pinned.Landed as
351464ceonrefactor(row 74 done at rev 546, queue commitb1c4acf3).17ea0dbc.queue review verify-commit 74matches the candidate.a55915f0plus the patch: webui 61/61, control-board 124/124, cli 113/113; test-auth 15, conductor 17, config 24, extension-package 18, foundation 44, queue 27, release 14, task 98, all 0 failed. test-discord failed one timing test (engine grace, 280 ms at load 30) on the sequential run and passed 66/66 on a solo rerun; the patch touches no discord file.