---
read_when:
    - คุณต้องการใช้งาน Gateway จากเบราว์เซอร์
    - คุณต้องการเข้าถึง Tailnet โดยไม่ใช้อุโมงค์ SSH
sidebarTitle: Control UI
summary: ส่วนติดต่อผู้ใช้สำหรับควบคุมผ่านเบราว์เซอร์สำหรับ Gateway (แชต, โหนด, การกำหนดค่า)
title: ส่วนติดต่อผู้ใช้สำหรับควบคุม
x-i18n:
    generated_at: "2026-05-11T20:40:45Z"
    model: gpt-5.5
    provider: openai
    source_hash: d0033b2666fe76bd23d5585d05b39fdd33f8d15d4e7c16561b5cfd0e75b8d22e
    source_path: web/control-ui.md
    workflow: 16
---

Control UI เป็นแอปหน้าเดียว **Vite + Lit** ขนาดเล็กที่ Gateway ให้บริการ:

- ค่าเริ่มต้น: `http://<host>:18789/`
- คำนำหน้าเสริม: ตั้งค่า `gateway.controlUi.basePath` (เช่น `/openclaw`)

แอปนี้สื่อสาร **โดยตรงกับ Gateway WebSocket** บนพอร์ตเดียวกัน

## เปิดอย่างรวดเร็ว (ภายในเครื่อง)

หาก Gateway กำลังทำงานบนคอมพิวเตอร์เครื่องเดียวกัน ให้เปิด:

- [http://127.0.0.1:18789/](http://127.0.0.1:18789/) (หรือ [http://localhost:18789/](http://localhost:18789/))

หากหน้าเว็บโหลดไม่สำเร็จ ให้เริ่ม Gateway ก่อน: `openclaw gateway`

ระบบส่งข้อมูลยืนยันตัวตนระหว่างการ handshake ของ WebSocket ผ่าน:

- `connect.params.auth.token`
- `connect.params.auth.password`
- ส่วนหัวข้อมูลประจำตัวของ Tailscale Serve เมื่อ `gateway.auth.allowTailscale: true`
- ส่วนหัวข้อมูลประจำตัวของ trusted-proxy เมื่อ `gateway.auth.mode: "trusted-proxy"`

แผงการตั้งค่าแดชบอร์ดจะเก็บโทเค็นไว้สำหรับเซสชันแท็บเบราว์เซอร์ปัจจุบันและ URL Gateway ที่เลือก ส่วนรหัสผ่านจะไม่ถูกจัดเก็บ โดยปกติ onboarding จะสร้างโทเค็น Gateway สำหรับการยืนยันตัวตนแบบ shared-secret เมื่อเชื่อมต่อครั้งแรก แต่การยืนยันตัวตนด้วยรหัสผ่านก็ใช้ได้เช่นกันเมื่อ `gateway.auth.mode` เป็น `"password"`

## การจับคู่อุปกรณ์ (การเชื่อมต่อครั้งแรก)

เมื่อคุณเชื่อมต่อกับ Control UI จากเบราว์เซอร์หรืออุปกรณ์ใหม่ Gateway มักจะต้องมี **การอนุมัติการจับคู่แบบครั้งเดียว** นี่เป็นมาตรการรักษาความปลอดภัยเพื่อป้องกันการเข้าถึงโดยไม่ได้รับอนุญาต

**สิ่งที่คุณจะเห็น:** "disconnected (1008): pairing required"

<Steps>
  <Step title="แสดงรายการคำขอที่รอดำเนินการ">
    ```bash
    openclaw devices list
    ```
  </Step>
  <Step title="อนุมัติด้วย request ID">
    ```bash
    openclaw devices approve <requestId>
    ```
  </Step>
</Steps>

หากเบราว์เซอร์ลองจับคู่อีกครั้งพร้อมรายละเอียดการยืนยันตัวตนที่เปลี่ยนไป (role/scopes/public key) คำขอที่รอดำเนินการก่อนหน้าจะถูกแทนที่ และจะสร้าง `requestId` ใหม่ ให้รัน `openclaw devices list` อีกครั้งก่อนอนุมัติ

หากเบราว์เซอร์ถูกจับคู่อยู่แล้ว และคุณเปลี่ยนจากสิทธิ์อ่านเป็นสิทธิ์เขียน/admin ระบบจะถือว่านี่เป็นการอัปเกรดการอนุมัติ ไม่ใช่การเชื่อมต่อใหม่แบบเงียบ OpenClaw จะคงการอนุมัติเก่าไว้ บล็อกการเชื่อมต่อใหม่ที่มีสิทธิ์กว้างขึ้น และขอให้คุณอนุมัติชุด scope ใหม่อย่างชัดเจน

เมื่ออนุมัติแล้ว ระบบจะจดจำอุปกรณ์นั้น และจะไม่ต้องอนุมัติซ้ำ เว้นแต่คุณจะเพิกถอนด้วย `openclaw devices revoke --device <id> --role <role>` ดู [Devices CLI](/th/cli/devices) สำหรับการหมุนเวียนและการเพิกถอนโทเค็น

<Note>
- การเชื่อมต่อเบราว์เซอร์ผ่าน local loopback โดยตรง (`127.0.0.1` / `localhost`) จะได้รับการอนุมัติอัตโนมัติ
- Tailscale Serve สามารถข้ามรอบการจับคู่สำหรับเซสชันผู้ปฏิบัติงาน Control UI ได้เมื่อ `gateway.auth.allowTailscale: true`, ยืนยันตัวตน Tailscale สำเร็จ และเบราว์เซอร์แสดงข้อมูลประจำตัวอุปกรณ์ของตน
- การ bind กับ Tailnet โดยตรง, การเชื่อมต่อเบราว์เซอร์ผ่าน LAN และโปรไฟล์เบราว์เซอร์ที่ไม่มีข้อมูลประจำตัวอุปกรณ์ ยังต้องได้รับการอนุมัติอย่างชัดเจน
- แต่ละโปรไฟล์เบราว์เซอร์จะสร้าง ID อุปกรณ์ที่ไม่ซ้ำกัน ดังนั้นการสลับเบราว์เซอร์หรือล้างข้อมูลเบราว์เซอร์จะต้องจับคู่ใหม่

</Note>

## ข้อมูลประจำตัวส่วนบุคคล (เฉพาะเบราว์เซอร์)

Control UI รองรับข้อมูลประจำตัวส่วนบุคคลต่อเบราว์เซอร์ (ชื่อที่แสดงและอวตาร) ซึ่งแนบกับข้อความขาออกเพื่อระบุที่มาในเซสชันที่แชร์ ข้อมูลนี้อยู่ในพื้นที่จัดเก็บของเบราว์เซอร์ ถูกจำกัดขอบเขตไว้ที่โปรไฟล์เบราว์เซอร์ปัจจุบัน และจะไม่ซิงก์ไปยังอุปกรณ์อื่นหรือเก็บถาวรฝั่งเซิร์ฟเวอร์ นอกเหนือจาก metadata ผู้เขียนตามปกติใน transcript ของข้อความที่คุณส่งจริง การล้างข้อมูลไซต์หรือสลับเบราว์เซอร์จะรีเซ็ตค่าให้ว่าง

รูปแบบเฉพาะเบราว์เซอร์เดียวกันนี้ใช้กับการแทนที่อวตารของผู้ช่วยด้วย อวตารผู้ช่วยที่อัปโหลดจะซ้อนทับข้อมูลประจำตัวที่ Gateway resolve แล้วบนเบราว์เซอร์ภายในเครื่องเท่านั้น และจะไม่ส่งวนกลับผ่าน `config.patch` ฟิลด์ config ที่แชร์ชื่อ `ui.assistant.avatar` ยังพร้อมใช้งานสำหรับไคลเอนต์ที่ไม่ใช่ UI ซึ่งเขียนฟิลด์นี้โดยตรง (เช่น Gateway แบบสคริปต์หรือแดชบอร์ดแบบกำหนดเอง)

## endpoint การตั้งค่ารันไทม์

Control UI ดึงการตั้งค่ารันไทม์จาก `/__openclaw/control-ui-config.json` endpoint นี้ถูกควบคุมด้วยการยืนยันตัวตน Gateway แบบเดียวกับพื้นผิว HTTP ส่วนที่เหลือ: เบราว์เซอร์ที่ไม่ได้ยืนยันตัวตนจะดึงข้อมูลไม่ได้ และการดึงข้อมูลสำเร็จต้องมีโทเค็น/รหัสผ่าน Gateway ที่ถูกต้องอยู่แล้ว, ข้อมูลประจำตัว Tailscale Serve หรือข้อมูลประจำตัว trusted-proxy

## การรองรับภาษา

Control UI สามารถแปลตัวเองตาม locale ของเบราว์เซอร์ในการโหลดครั้งแรก หากต้องการเปลี่ยนในภายหลัง ให้เปิด **Overview -> Gateway Access -> Language** ตัวเลือก locale อยู่ในการ์ด Gateway Access ไม่ได้อยู่ใต้ Appearance

- locale ที่รองรับ: `en`, `zh-CN`, `zh-TW`, `pt-BR`, `de`, `es`, `ja-JP`, `ko`, `fr`, `ar`, `it`, `tr`, `uk`, `id`, `pl`, `th`, `vi`, `nl`, `fa`
- คำแปลที่ไม่ใช่ภาษาอังกฤษจะถูกโหลดแบบ lazy-load ในเบราว์เซอร์
- locale ที่เลือกจะถูกบันทึกในพื้นที่จัดเก็บของเบราว์เซอร์และนำมาใช้ซ้ำในการเข้าชมครั้งถัดไป
- คีย์คำแปลที่ขาดหายจะย้อนกลับไปใช้ภาษาอังกฤษ

คำแปลเอกสารถูกสร้างสำหรับชุด locale ที่ไม่ใช่ภาษาอังกฤษชุดเดียวกัน แต่ตัวเลือกภาษาของไซต์เอกสารที่ Mintlify มีในตัวจะจำกัดอยู่ที่รหัส locale ที่ Mintlify ยอมรับ เอกสารภาษาไทย (`th`) และเปอร์เซีย (`fa`) ยังคงถูกสร้างใน repo สำหรับเผยแพร่ แต่อาจยังไม่ปรากฏในตัวเลือกนั้นจนกว่า Mintlify จะรองรับรหัสเหล่านั้น

## ธีม Appearance

แผง Appearance จะคงธีมในตัว Claw, Knot และ Dash รวมถึงช่องนำเข้า tweakcn เฉพาะเบราว์เซอร์หนึ่งช่อง หากต้องการนำเข้าธีม ให้เปิด [tweakcn editor](https://tweakcn.com/editor/theme), เลือกหรือสร้างธีม, คลิก **Share** แล้ววางลิงก์ธีมที่คัดลอกไว้ใน Appearance ตัวนำเข้ายังรองรับ URL registry แบบ `https://tweakcn.com/r/themes/<id>`, URL editor เช่น `https://tweakcn.com/editor/theme?theme=amethyst-haze`, พาธสัมพัทธ์ `/themes/<id>`, ID ธีมดิบ และชื่อธีมเริ่มต้น เช่น `amethyst-haze`

ธีมที่นำเข้าจะถูกเก็บไว้เฉพาะในโปรไฟล์เบราว์เซอร์ปัจจุบันเท่านั้น ธีมจะไม่ถูกเขียนลง config ของ Gateway และไม่ซิงก์ข้ามอุปกรณ์ การแทนที่ธีมที่นำเข้าจะอัปเดตช่องภายในเครื่องหนึ่งช่อง การล้างธีมจะสลับธีมที่ใช้งานกลับไปเป็น Claw หากธีมที่นำเข้าเคยถูกเลือกอยู่

## สิ่งที่ทำได้ (วันนี้)

<AccordionGroup>
  <Accordion title="แชทและพูดคุย">
    - แชทกับโมเดลผ่าน Gateway WS (`chat.history`, `chat.send`, `chat.abort`, `chat.inject`)
    - การรีเฟรชประวัติแชทจะขอหน้าต่างล่าสุดที่จำกัดขอบเขต พร้อมเพดานข้อความต่อข้อความ เพื่อให้เซสชันขนาดใหญ่ไม่บังคับให้เบราว์เซอร์เรนเดอร์ payload transcript เต็มก่อนที่แชทจะพร้อมใช้งาน
    - พูดคุยผ่านเซสชันแบบเรียลไทม์ของเบราว์เซอร์ OpenAI ใช้ WebRTC โดยตรง, Google Live ใช้โทเค็นเบราว์เซอร์แบบใช้ครั้งเดียวที่ถูกจำกัดผ่าน WebSocket และ Plugin เสียงเรียลไทม์แบบ backend-only ใช้ transport relay ของ Gateway เซสชัน provider ที่ไคลเอนต์เป็นเจ้าของเริ่มด้วย `talk.client.create`; เซสชัน Gateway relay เริ่มด้วย `talk.session.create` relay จะเก็บข้อมูลรับรอง provider ไว้บน Gateway ขณะที่เบราว์เซอร์สตรีมไมโครโฟน PCM ผ่าน `talk.session.appendAudio` และส่งต่อการเรียก tool ของ provider `openclaw_agent_consult` ผ่าน `talk.client.toolCall` เพื่อใช้นโยบาย Gateway และโมเดล OpenClaw ที่กำหนดค่าไว้ซึ่งใหญ่กว่า
    - สตรีมการเรียก tool + การ์ดผลลัพธ์ tool แบบสดใน Chat (เหตุการณ์ agent)

  </Accordion>
  <Accordion title="ช่องทาง, instances, sessions, dreams">
    - ช่องทาง: สถานะช่องทางในตัวและช่องทางจาก Plugin ที่ bundled/ภายนอก, การเข้าสู่ระบบด้วย QR และ config ต่อช่องทาง (`channels.status`, `web.login.*`, `config.patch`)
    - การรีเฟรช probe ของช่องทางจะคง snapshot ก่อนหน้าให้มองเห็นอยู่ขณะที่การตรวจสอบ provider ที่ช้ากำลังเสร็จสิ้น และ snapshot บางส่วนจะถูกติดป้ายเมื่อ probe หรือ audit เกินงบเวลา UI
    - Instances: รายการ presence + รีเฟรช (`system-presence`)
    - Sessions: แสดงรายการเซสชัน configured-agent ตามค่าเริ่มต้น, fallback จากคีย์เซสชัน agent ที่ไม่ได้กำหนดค่าและค้างอยู่ และใช้การ override model/thinking/fast/verbose/trace/reasoning ต่อเซสชัน (`sessions.list`, `sessions.patch`)
    - Dreams: สถานะ dreaming, สวิตช์ enable/disable และตัวอ่าน Dream Diary (`doctor.memory.status`, `doctor.memory.dreamDiary`, `config.patch`)

  </Accordion>
  <Accordion title="Cron, skills, nodes, การอนุมัติ exec">
    - งาน Cron: list/add/edit/run/enable/disable + ประวัติการรัน (`cron.*`)
    - Skills: สถานะ, enable/disable, ติดตั้ง, อัปเดต API key (`skills.*`)
    - Nodes: list + caps (`node.list`)
    - การอนุมัติ exec: แก้ไข allowlist ของ gateway หรือ node + นโยบาย ask สำหรับ `exec host=gateway/node` (`exec.approvals.*`)

  </Accordion>
  <Accordion title="Config">
    - ดู/แก้ไข `~/.openclaw/openclaw.json` (`config.get`, `config.set`)
    - Apply + restart พร้อม validation (`config.apply`) และปลุกเซสชันล่าสุดที่ใช้งานอยู่
    - การเขียนมี guard แบบ base-hash เพื่อป้องกันการเขียนทับการแก้ไขที่เกิดขึ้นพร้อมกัน
    - การเขียน (`config.set`/`config.apply`/`config.patch`) จะ preflight การ resolve SecretRef ที่ active สำหรับ refs ใน payload config ที่ส่งมา; refs ที่ส่งมาและ active แต่ resolve ไม่ได้จะถูกปฏิเสธก่อนเขียน
    - Schema + การเรนเดอร์ฟอร์ม (`config.schema` / `config.schema.lookup`, รวมถึงฟิลด์ `title` / `description`, UI hints ที่ match, สรุป child โดยตรง, metadata เอกสารบนโหนด object/wildcard/array/composition แบบซ้อน รวมถึง schema ของ Plugin + ช่องทางเมื่อมี); Raw JSON editor พร้อมใช้งานเฉพาะเมื่อ snapshot มี raw round-trip ที่ปลอดภัย
    - หาก snapshot ไม่สามารถ round-trip ข้อความดิบได้อย่างปลอดภัย Control UI จะบังคับโหมด Form และปิดใช้โหมด Raw สำหรับ snapshot นั้น
    - Raw JSON editor "Reset to saved" จะรักษารูปทรงที่เขียนแบบ raw ไว้ (การจัดรูปแบบ, ความคิดเห็น, layout ของ `$include`) แทนการเรนเดอร์ snapshot ที่ flatten ใหม่ เพื่อให้การแก้ไขภายนอกยังอยู่รอดหลัง reset เมื่อ snapshot สามารถ round-trip ได้อย่างปลอดภัย
    - ค่า object แบบ Structured SecretRef จะแสดงเป็นแบบอ่านอย่างเดียวใน input ข้อความของฟอร์ม เพื่อป้องกันความเสียหายจากการแปลง object เป็น string โดยไม่ตั้งใจ

  </Accordion>
  <Accordion title="Debug, logs, update">
    - Debug: snapshot ของ status/health/models + event log + การเรียก RPC แบบ manual (`status`, `health`, `models.list`)
    - event log มีเวลาของการรีเฟรช/RPC ใน Control UI, เวลาการเรนเดอร์แชท/config ที่ช้า และรายการความตอบสนองของเบราว์เซอร์สำหรับ animation frame ที่ยาวหรืองานที่ยาว เมื่อเบราว์เซอร์เปิดเผยชนิดรายการ PerformanceObserver เหล่านั้น
    - Logs: tail สดของไฟล์ log ของ gateway พร้อม filter/export (`logs.tail`)
    - Update: รันการอัปเดต package/git + restart (`update.run`) พร้อมรายงานการ restart จากนั้น poll `update.status` หลังเชื่อมต่อใหม่เพื่อตรวจสอบเวอร์ชัน gateway ที่กำลังรันอยู่

  </Accordion>
  <Accordion title="หมายเหตุแผงงาน Cron">
    - สำหรับงานแบบ isolated ค่า delivery เริ่มต้นคือประกาศสรุป คุณสามารถสลับเป็น none ได้หากต้องการรันภายในเท่านั้น
    - ฟิลด์ช่องทาง/target จะปรากฏเมื่อเลือก announce
    - โหมด Webhook ใช้ `delivery.mode = "webhook"` โดยตั้งค่า `delivery.to` เป็น URL webhook HTTP(S) ที่ถูกต้อง
    - สำหรับงาน main-session มีโหมด delivery แบบ webhook และ none ให้ใช้
    - ตัวควบคุมแก้ไขขั้นสูงประกอบด้วย delete-after-run, clear agent override, ตัวเลือก cron exact/stagger, การ override agent model/thinking และสวิตช์ best-effort delivery
    - การตรวจสอบความถูกต้องของฟอร์มเป็นแบบ inline พร้อมข้อผิดพลาดระดับฟิลด์; ค่าที่ไม่ถูกต้องจะปิดใช้ปุ่มบันทึกจนกว่าจะแก้ไข
    - ตั้งค่า `cron.webhookToken` เพื่อส่ง bearer token เฉพาะ หากละไว้ webhook จะถูกส่งโดยไม่มีส่วนหัว auth
    - fallback ที่เลิกใช้แล้ว: งาน legacy ที่จัดเก็บไว้พร้อม `notify: true` ยังสามารถใช้ `cron.webhook` ได้จนกว่าจะ migrate

  </Accordion>
</AccordionGroup>

## พฤติกรรมของ Chat

<AccordionGroup>
  <Accordion title="Send and history semantics">
    - `chat.send` เป็นแบบ **ไม่บล็อกการทำงาน**: ยืนยันรับทันทีด้วย `{ runId, status: "started" }` และคำตอบจะสตรีมผ่านเหตุการณ์ `chat`
    - การอัปโหลดแชตรองรับรูปภาพและไฟล์ที่ไม่ใช่วิดีโอ รูปภาพจะคงเส้นทางรูปภาพดั้งเดิมไว้ ส่วนไฟล์อื่นจะถูกจัดเก็บเป็นสื่อที่จัดการแล้วและแสดงในประวัติเป็นลิงก์ไฟล์แนบ
    - การส่งซ้ำด้วย `idempotencyKey` เดิมจะส่งคืน `{ status: "in_flight" }` ขณะกำลังทำงาน และ `{ status: "ok" }` หลังเสร็จสิ้น
    - คำตอบของ `chat.history` ถูกจำกัดขนาดเพื่อความปลอดภัยของ UI เมื่อรายการถอดความมีขนาดใหญ่เกินไป Gateway อาจตัดทอนฟิลด์ข้อความยาว ๆ ละเว้นบล็อกข้อมูลเมตาขนาดใหญ่ และแทนที่ข้อความที่ใหญ่เกินด้วยตัวแทน (`[chat.history omitted: message too large]`)
    - รูปภาพจากผู้ช่วย/ที่สร้างขึ้นจะถูกเก็บถาวรเป็นการอ้างอิงสื่อที่จัดการแล้ว และส่งกลับผ่าน URL สื่อของ Gateway ที่ผ่านการยืนยันตัวตนแล้ว ดังนั้นการโหลดซ้ำจึงไม่ต้องพึ่งพาเพย์โหลดรูปภาพ base64 ดิบที่ยังคงอยู่ในคำตอบประวัติแชต
    - เมื่อเรนเดอร์ `chat.history` Control UI จะลบแท็กคำสั่งแบบอินไลน์ที่มีไว้เพื่อการแสดงผลเท่านั้นออกจากข้อความผู้ช่วยที่มองเห็นได้ (เช่น `[[reply_to_*]]` และ `[[audio_as_voice]]`), เพย์โหลด XML ของการเรียกเครื่องมือแบบข้อความล้วน (รวมถึง `<tool_call>...</tool_call>`, `<function_call>...</function_call>`, `<tool_calls>...</tool_calls>`, `<function_calls>...</function_calls>` และบล็อกการเรียกเครื่องมือที่ถูกตัดทอน), และโทเค็นควบคุมโมเดล ASCII/เต็มความกว้างที่รั่วไหลมา และละเว้นรายการของผู้ช่วยที่ข้อความที่มองเห็นได้ทั้งหมดเป็นเพียงโทเค็นเงียบที่ตรงกันทุกตัวอักษร `NO_REPLY` / `no_reply` หรือโทเค็นยืนยัน Heartbeat `HEARTBEAT_OK`
    - ระหว่างการส่งที่กำลังทำงานและการรีเฟรชประวัติครั้งสุดท้าย มุมมองแชตจะยังคงแสดงข้อความผู้ใช้/ผู้ช่วยแบบมองโลกในแง่ดีในเครื่องไว้ หาก `chat.history` ส่งคืนสแนปช็อตที่เก่ากว่าชั่วครู่; บันทึกถอดความแบบบัญญัติจะมาแทนที่ข้อความในเครื่องเหล่านั้นเมื่อประวัติ Gateway ตามทัน
    - เหตุการณ์ `chat` สดคือสถานะการส่งมอบ ส่วน `chat.history` ถูกสร้างใหม่จากบันทึกถอดความเซสชันที่คงทน หลังเหตุการณ์เครื่องมือขั้นสุดท้าย Control UI จะโหลดประวัติใหม่และผสานเฉพาะส่วนท้ายแบบมองโลกในแง่ดีขนาดเล็กเท่านั้น; ขอบเขตบันทึกถอดความมีเอกสารไว้ใน [WebChat](/th/web/webchat)
    - `chat.inject` เพิ่มบันทึกของผู้ช่วยต่อท้ายบันทึกถอดความเซสชันและบรอดแคสต์เหตุการณ์ `chat` สำหรับการอัปเดตเฉพาะ UI (ไม่มีการรันเอเจนต์ ไม่มีการส่งมอบช่องทาง)
    - ส่วนหัวแชตแสดงตัวกรองเอเจนต์ก่อนตัวเลือกเซสชัน และตัวเลือกเซสชันจะถูกจำกัดขอบเขตตามเอเจนต์ที่เลือก การสลับเอเจนต์จะแสดงเฉพาะเซสชันที่ผูกกับเอเจนต์นั้น และย้อนกลับไปใช้เซสชันหลักของเอเจนต์นั้นเมื่อยังไม่มีเซสชันแดชบอร์ดที่บันทึกไว้
    - บนความกว้างเดสก์ท็อป ตัวควบคุมแชตจะอยู่ในแถวเดียวแบบกะทัดรัดและยุบลงขณะเลื่อนลงในบันทึกถอดความ; การเลื่อนขึ้น กลับไปด้านบน หรือไปถึงด้านล่างจะคืนค่าตัวควบคุม
    - ข้อความซ้ำที่อยู่ติดกันและมีเฉพาะข้อความจะแสดงเป็นบับเบิลเดียวพร้อมป้ายจำนวน ข้อความที่มีรูปภาพ ไฟล์แนบ เอาต์พุตเครื่องมือ หรือพรีวิวแคนวาสจะไม่ถูกยุบ
    - ตัวเลือกโมเดลและการคิดในส่วนหัวแชตจะแพตช์เซสชันที่ใช้งานอยู่ทันทีผ่าน `sessions.patch`; สิ่งเหล่านี้เป็นการแทนที่เซสชันแบบคงอยู่ ไม่ใช่ตัวเลือกการส่งเฉพาะเทิร์นเดียว
    - หากคุณส่งข้อความขณะที่การเปลี่ยนตัวเลือกโมเดลสำหรับเซสชันเดียวกันยังบันทึกอยู่ ตัวเขียนข้อความจะรอให้แพตช์เซสชันนั้นเสร็จก่อนเรียก `chat.send` เพื่อให้การส่งใช้โมเดลที่เลือก
    - การพิมพ์ `/new` ใน Control UI จะสร้างและสลับไปยังเซสชันแดชบอร์ดใหม่แบบเดียวกับ New Chat ยกเว้นเมื่อมีการกำหนดค่า `session.dmScope: "main"` และพาเรนต์ปัจจุบันเป็นเซสชันหลักของเอเจนต์; ในกรณีนั้นจะรีเซ็ตเซสชันหลักในตำแหน่งเดิม การพิมพ์ `/reset` จะคงการรีเซ็ตในตำแหน่งเดิมแบบชัดเจนของ Gateway สำหรับเซสชันปัจจุบัน
    - ตัวเลือกโมเดลแชตขอมุมมองโมเดลที่กำหนดค่าไว้ของ Gateway หากมี `agents.defaults.models` รายการอนุญาตนั้นจะขับเคลื่อนตัวเลือก รวมถึงรายการ `provider/*` ที่ทำให้แคตตาล็อกที่จำกัดตามผู้ให้บริการยังคงเป็นแบบไดนามิก มิฉะนั้น ตัวเลือกจะแสดงรายการ `models.providers.*.models` แบบชัดเจน พร้อมผู้ให้บริการที่มีการยืนยันตัวตนที่ใช้งานได้ แคตตาล็อกแบบเต็มยังคงพร้อมใช้งานผ่าน RPC ดีบัก `models.list` พร้อม `view: "all"`
    - เมื่อรายงานการใช้งานเซสชัน Gateway ใหม่มีโทเค็นบริบทปัจจุบัน พื้นที่ตัวเขียนข้อความแชตจะแสดงตัวบ่งชี้การใช้งานบริบทแบบกะทัดรัด โดยจะเปลี่ยนเป็นสไตล์คำเตือนเมื่อมีแรงกดดันต่อบริบทสูง และในระดับ Compaction ที่แนะนำ จะแสดงปุ่มแบบกะทัดรัดที่รันเส้นทาง Compaction เซสชันตามปกติ สแนปช็อตโทเค็นที่ล้าสมัยจะถูกซ่อนไว้จนกว่า Gateway จะรายงานการใช้งานใหม่อีกครั้ง

  </Accordion>
  <Accordion title="Talk mode (browser realtime)">
    โหมดพูดคุยใช้ผู้ให้บริการเสียงเรียลไทม์ที่ลงทะเบียนไว้ กำหนดค่า OpenAI ด้วย `talk.realtime.provider: "openai"` พร้อม `talk.realtime.providers.openai.apiKey`, `OPENAI_API_KEY` หรือโปรไฟล์ OAuth `openai-codex`; กำหนดค่า Google ด้วย `talk.realtime.provider: "google"` พร้อม `talk.realtime.providers.google.apiKey` เบราว์เซอร์จะไม่ได้รับคีย์ API ผู้ให้บริการมาตรฐาน OpenAI ได้รับความลับไคลเอนต์ Realtime ชั่วคราวสำหรับ WebRTC Google Live ได้รับโทเค็นการยืนยันตัวตน Live API แบบจำกัดและใช้ครั้งเดียวสำหรับเซสชัน WebSocket ของเบราว์เซอร์ โดยมีคำสั่งและประกาศเครื่องมือที่ถูกล็อกไว้ในโทเค็นโดย Gateway ผู้ให้บริการที่เปิดเผยเฉพาะสะพานเรียลไทม์ฝั่งแบ็กเอนด์จะทำงานผ่านทรานสปอร์ตรีเลย์ของ Gateway ดังนั้นข้อมูลประจำตัวและซ็อกเก็ตผู้ขายจะอยู่ฝั่งเซิร์ฟเวอร์ ขณะที่เสียงจากเบราว์เซอร์เคลื่อนผ่าน RPC ของ Gateway ที่ผ่านการยืนยันตัวตน พรอมป์เซสชัน Realtime ถูกประกอบโดย Gateway; `talk.client.create` ไม่ยอมรับการแทนที่คำสั่งจากผู้เรียก

    ตัวเขียนข้อความแชตมีปุ่มตัวเลือกพูดคุยถัดจากปุ่มเริ่ม/หยุดพูดคุย ตัวเลือกจะมีผลกับเซสชันพูดคุยถัดไป และสามารถแทนที่ผู้ให้บริการ ทรานสปอร์ต โมเดล เสียง ความพยายามในการให้เหตุผล เกณฑ์ VAD ระยะเวลาเงียบ และการเติมนำหน้าได้ เมื่อตัวเลือกว่าง Gateway จะใช้ค่าเริ่มต้นที่กำหนดค่าไว้เมื่อมี หรือค่าเริ่มต้นของผู้ให้บริการ การเลือกรีเลย์ Gateway จะบังคับใช้เส้นทางรีเลย์แบ็กเอนด์; การเลือก WebRTC จะคงให้เซสชันเป็นของไคลเอนต์ และล้มเหลวแทนที่จะย้อนกลับไปใช้รีเลย์อย่างเงียบ ๆ หากผู้ให้บริการไม่สามารถสร้างเซสชันเบราว์เซอร์ได้

    ในตัวเขียนข้อความแชต ตัวควบคุมพูดคุยคือปุ่มรูปคลื่นถัดจากปุ่มถอดคำพูดจากไมโครโฟน เมื่อการพูดคุยเริ่มต้น แถวสถานะของตัวเขียนข้อความจะแสดง `Connecting Talk...` จากนั้นแสดง `Talk live` ขณะที่เสียงเชื่อมต่ออยู่ หรือ `Asking OpenClaw...` ขณะการเรียกเครื่องมือเรียลไทม์กำลังปรึกษาโมเดลขนาดใหญ่กว่าที่กำหนดค่าไว้ผ่าน `talk.client.toolCall`

    การทดสอบควันแบบสดสำหรับผู้ดูแล: `OPENAI_API_KEY=... GEMINI_API_KEY=... node --import tsx scripts/dev/realtime-talk-live-smoke.ts` ตรวจสอบสะพาน WebSocket แบ็กเอนด์ของ OpenAI, การแลกเปลี่ยน SDP ของ WebRTC เบราว์เซอร์ OpenAI, การตั้งค่า WebSocket เบราว์เซอร์ด้วยโทเค็นจำกัดของ Google Live และอะแดปเตอร์เบราว์เซอร์รีเลย์ Gateway พร้อมสื่อไมโครโฟนจำลอง คำสั่งจะแสดงเฉพาะสถานะผู้ให้บริการและไม่บันทึกความลับ

  </Accordion>
  <Accordion title="Stop and abort">
    - คลิก **หยุด** (เรียก `chat.abort`)
    - ขณะที่การรันกำลังทำงาน การติดตามผลตามปกติจะเข้าคิว คลิก **นำทาง** บนข้อความที่เข้าคิวเพื่อฉีดการติดตามผลนั้นเข้าสู่เทิร์นที่กำลังรัน
    - พิมพ์ `/stop` (หรือวลีหยุดแบบเดี่ยว เช่น `stop`, `stop action`, `stop run`, `stop openclaw`, `please stop`) เพื่อยกเลิกนอกแบนด์
    - `chat.abort` รองรับ `{ sessionKey }` (ไม่มี `runId`) เพื่อยกเลิกการรันที่ทำงานอยู่ทั้งหมดสำหรับเซสชันนั้น

  </Accordion>
  <Accordion title="Abort partial retention">
    - เมื่อการรันถูกยกเลิก ข้อความผู้ช่วยบางส่วนยังสามารถแสดงใน UI ได้
    - Gateway จะคงข้อความผู้ช่วยบางส่วนที่ถูกยกเลิกไว้ในประวัติบันทึกถอดความเมื่อมีเอาต์พุตที่บัฟเฟอร์ไว้
    - รายการที่คงไว้มีข้อมูลเมตาการยกเลิก เพื่อให้ผู้ใช้บันทึกถอดความแยกข้อความบางส่วนจากการยกเลิกออกจากเอาต์พุตการเสร็จสมบูรณ์ตามปกติได้

  </Accordion>
</AccordionGroup>

## การติดตั้ง PWA และ Web Push

Control UI มาพร้อม `manifest.webmanifest` และ service worker ดังนั้นเบราว์เซอร์สมัยใหม่จึงติดตั้งเป็น PWA แบบสแตนด์อโลนได้ Web Push ช่วยให้ Gateway ปลุก PWA ที่ติดตั้งแล้วด้วยการแจ้งเตือนได้ แม้แท็บหรือหน้าต่างเบราว์เซอร์ไม่ได้เปิดอยู่

| พื้นผิว                                               | สิ่งที่ทำ                                                       |
| ----------------------------------------------------- | ------------------------------------------------------------------ |
| `ui/public/manifest.webmanifest`                      | แมนิเฟสต์ PWA เบราว์เซอร์จะเสนอ "ติดตั้งแอป" เมื่อเข้าถึงได้   |
| `ui/public/sw.js`                                     | service worker ที่จัดการเหตุการณ์ `push` และการคลิกการแจ้งเตือน |
| `push/vapid-keys.json` (ใต้ไดเรกทอรีสถานะ OpenClaw) | คู่คีย์ VAPID ที่สร้างอัตโนมัติ ใช้ลงนามเพย์โหลด Web Push       |
| `push/web-push-subscriptions.json`                    | endpoint การสมัครรับข้อมูลของเบราว์เซอร์ที่คงไว้                          |

แทนที่คู่คีย์ VAPID ผ่านตัวแปรสภาพแวดล้อมบนกระบวนการ Gateway เมื่อคุณต้องการตรึงคีย์ (สำหรับการปรับใช้หลายโฮสต์ การหมุนเวียนความลับ หรือการทดสอบ):

- `OPENCLAW_VAPID_PUBLIC_KEY`
- `OPENCLAW_VAPID_PRIVATE_KEY`
- `OPENCLAW_VAPID_SUBJECT` (ค่าเริ่มต้นคือ `mailto:openclaw@localhost`)

Control UI ใช้เมธอด Gateway ที่จำกัดด้วยขอบเขตเหล่านี้เพื่อลงทะเบียนและทดสอบการสมัครรับข้อมูลของเบราว์เซอร์:

- `push.web.vapidPublicKey` — ดึงคีย์สาธารณะ VAPID ที่ใช้งานอยู่
- `push.web.subscribe` — ลงทะเบียน `endpoint` พร้อม `keys.p256dh`/`keys.auth`
- `push.web.unsubscribe` — ลบ endpoint ที่ลงทะเบียนไว้
- `push.web.test` — ส่งการแจ้งเตือนทดสอบไปยังการสมัครรับข้อมูลของผู้เรียก

<Note>
Web Push เป็นอิสระจากเส้นทางรีเลย์ APNS ของ iOS (ดู [การกำหนดค่า](/th/gateway/configuration) สำหรับการพุชที่มีรีเลย์รองรับ) และเมธอด `push.test` ที่มีอยู่ ซึ่งมุ่งเป้าไปที่การจับคู่มือถือแบบเนทีฟ
</Note>

## เอ็มเบดที่โฮสต์

ข้อความผู้ช่วยสามารถเรนเดอร์เนื้อหาเว็บที่โฮสต์แบบอินไลน์ด้วยชอร์ตโค้ด `[embed ...]` นโยบาย sandbox ของ iframe ถูกควบคุมโดย `gateway.controlUi.embedSandbox`:

<Tabs>
  <Tab title="strict">
    ปิดใช้งานการเรียกใช้สคริปต์ภายในเอ็มเบดที่โฮสต์
  </Tab>
  <Tab title="scripts (default)">
    อนุญาตเอ็มเบดแบบโต้ตอบได้พร้อมคงการแยก origin; นี่คือค่าเริ่มต้นและโดยทั่วไปเพียงพอสำหรับเกม/วิดเจ็ตเบราว์เซอร์แบบจบในตัว
  </Tab>
  <Tab title="trusted">
    เพิ่ม `allow-same-origin` บน `allow-scripts` สำหรับเอกสารในไซต์เดียวกันที่ตั้งใจต้องใช้สิทธิ์ที่สูงกว่า
  </Tab>
</Tabs>

ตัวอย่าง:

```json5
{
  gateway: {
    controlUi: {
      embedSandbox: "scripts",
    },
  },
}
```

<Warning>
ใช้ `trusted` เฉพาะเมื่อเอกสารที่ฝังต้องการพฤติกรรม same-origin จริง ๆ สำหรับเกมและแคนวาสโต้ตอบที่เอเจนต์สร้างส่วนใหญ่ `scripts` เป็นตัวเลือกที่ปลอดภัยกว่า
</Warning>

URL เอ็มเบด `http(s)` ภายนอกแบบสมบูรณ์ยังคงถูกบล็อกโดยค่าเริ่มต้น หากคุณตั้งใจต้องการให้ `[embed url="https://..."]` โหลดหน้าจากบุคคลที่สาม ให้ตั้งค่า `gateway.controlUi.allowExternalEmbedUrls: true`

## ความกว้างข้อความแชต

ข้อความแชตที่จัดกลุ่มใช้ความกว้างสูงสุดค่าเริ่มต้นที่อ่านง่าย การปรับใช้บนจอกว้างสามารถแทนที่ได้โดยไม่ต้องแพตช์ CSS ที่บันเดิลมา ด้วยการตั้งค่า `gateway.controlUi.chatMessageMaxWidth`:

```json5
{
  gateway: {
    controlUi: {
      chatMessageMaxWidth: "min(1280px, 82%)",
    },
  },
}
```

ค่าจะถูกตรวจสอบก่อนถึงเบราว์เซอร์ ค่าที่รองรับรวมถึงความยาวและเปอร์เซ็นต์แบบธรรมดา เช่น `960px` หรือ `82%` พร้อมนิพจน์ความกว้างที่จำกัดอย่าง `min(...)`, `max(...)`, `clamp(...)`, `calc(...)` และ `fit-content(...)`

## การเข้าถึง Tailnet (แนะนำ)

<Tabs>
  <Tab title="Integrated Tailscale Serve (preferred)">
    คง Gateway ไว้บน loopback และให้ Tailscale Serve พร็อกซีด้วย HTTPS:

    ```bash
    openclaw gateway --tailscale serve
    ```

    เปิด:

    - `https://<magicdns>/` (หรือ `gateway.controlUi.basePath` ที่คุณกำหนดค่าไว้)

    โดยค่าเริ่มต้น คำขอ Control UI/WebSocket Serve สามารถยืนยันตัวตนผ่านส่วนหัวข้อมูลประจำตัวของ Tailscale (`tailscale-user-login`) ได้เมื่อ `gateway.auth.allowTailscale` เป็น `true` OpenClaw ตรวจสอบข้อมูลประจำตัวโดยแปลงที่อยู่ `x-forwarded-for` ด้วย `tailscale whois` และจับคู่กับส่วนหัว และจะยอมรับเฉพาะเมื่อคำขอเข้ามาที่ loopback พร้อมส่วนหัว `x-forwarded-*` ของ Tailscale เท่านั้น สำหรับเซสชันผู้ปฏิบัติงาน Control UI ที่มีข้อมูลประจำตัวอุปกรณ์ของเบราว์เซอร์ เส้นทาง Serve ที่ตรวจสอบแล้วนี้จะข้ามรอบการจับคู่อุปกรณ์ด้วยเช่นกัน ส่วนเบราว์เซอร์ที่ไม่มีอุปกรณ์และการเชื่อมต่อบทบาทโหนดยังคงทำตามการตรวจสอบอุปกรณ์ตามปกติ ตั้งค่า `gateway.auth.allowTailscale: false` หากคุณต้องการบังคับใช้ข้อมูลรับรอง shared-secret อย่างชัดเจนแม้กับทราฟฟิก Serve จากนั้นใช้ `gateway.auth.mode: "token"` หรือ `"password"`

    สำหรับเส้นทางข้อมูลประจำตัว Serve แบบ async นี้ ความพยายามยืนยันตัวตนที่ล้มเหลวสำหรับ IP ไคลเอนต์และขอบเขตการยืนยันตัวตนเดียวกันจะถูกจัดลำดับก่อนการเขียน rate-limit ดังนั้นการลองซ้ำที่ผิดพลาดพร้อมกันจากเบราว์เซอร์เดียวกันอาจแสดง `retry later` ในคำขอที่สอง แทนที่จะเกิด mismatch ธรรมดาสองรายการที่แข่งกันแบบขนาน

    <Warning>
    การยืนยันตัวตน Serve แบบไม่มีโทเคนถือว่าโฮสต์ gateway เชื่อถือได้ หากโค้ดโลคัลที่ไม่น่าเชื่อถืออาจทำงานบนโฮสต์นั้น ให้บังคับใช้การยืนยันตัวตนด้วยโทเคน/รหัสผ่าน
    </Warning>

  </Tab>
  <Tab title="Bind to tailnet + token">
    ```bash
    openclaw gateway --bind tailnet --token "$(openssl rand -hex 32)"
    ```

    จากนั้นเปิด:

    - `http://<tailscale-ip>:18789/` (หรือ `gateway.controlUi.basePath` ที่คุณกำหนดค่าไว้)

    วาง shared secret ที่ตรงกันลงในการตั้งค่า UI (ส่งเป็น `connect.params.auth.token` หรือ `connect.params.auth.password`)

  </Tab>
</Tabs>

## HTTP ที่ไม่ปลอดภัย

หากคุณเปิดแดชบอร์ดผ่าน HTTP ธรรมดา (`http://<lan-ip>` หรือ `http://<tailscale-ip>`) เบราว์เซอร์จะทำงานใน **บริบทที่ไม่ปลอดภัย** และบล็อก WebCrypto โดยค่าเริ่มต้น OpenClaw จะ **บล็อก** การเชื่อมต่อ Control UI ที่ไม่มีข้อมูลประจำตัวอุปกรณ์

ข้อยกเว้นที่บันทึกไว้:

- ความเข้ากันได้กับ HTTP ที่ไม่ปลอดภัยเฉพาะ localhost ด้วย `gateway.controlUi.allowInsecureAuth=true`
- การยืนยันตัวตน Control UI ของผู้ปฏิบัติงานสำเร็จผ่าน `gateway.auth.mode: "trusted-proxy"`
- กลไกฉุกเฉิน `gateway.controlUi.dangerouslyDisableDeviceAuth=true`

**วิธีแก้ไขที่แนะนำ:** ใช้ HTTPS (Tailscale Serve) หรือเปิด UI ในเครื่อง:

- `https://<magicdns>/` (Serve)
- `http://127.0.0.1:18789/` (บนโฮสต์ gateway)

<AccordionGroup>
  <Accordion title="Insecure-auth toggle behavior">
    ```json5
    {
      gateway: {
        controlUi: { allowInsecureAuth: true },
        bind: "tailnet",
        auth: { mode: "token", token: "replace-me" },
      },
    }
    ```

    `allowInsecureAuth` เป็นสวิตช์ความเข้ากันได้ในเครื่องเท่านั้น:

    - อนุญาตให้เซสชัน Control UI บน localhost ดำเนินต่อได้โดยไม่มีข้อมูลประจำตัวอุปกรณ์ในบริบท HTTP ที่ไม่ปลอดภัย
    - ไม่ข้ามการตรวจสอบการจับคู่
    - ไม่ผ่อนคลายข้อกำหนดข้อมูลประจำตัวอุปกรณ์ระยะไกล (ที่ไม่ใช่ localhost)

  </Accordion>
  <Accordion title="Break-glass only">
    ```json5
    {
      gateway: {
        controlUi: { dangerouslyDisableDeviceAuth: true },
        bind: "tailnet",
        auth: { mode: "token", token: "replace-me" },
      },
    }
    ```

    <Warning>
    `dangerouslyDisableDeviceAuth` ปิดใช้งานการตรวจสอบข้อมูลประจำตัวอุปกรณ์ของ Control UI และเป็นการลดระดับความปลอดภัยอย่างรุนแรง ให้ย้อนกลับโดยเร็วหลังใช้ในกรณีฉุกเฉิน
    </Warning>

  </Accordion>
  <Accordion title="Trusted-proxy note">
    - การยืนยันตัวตน trusted-proxy ที่สำเร็จสามารถอนุญาตเซสชัน Control UI ของ **ผู้ปฏิบัติงาน** โดยไม่มีข้อมูลประจำตัวอุปกรณ์ได้
    - สิ่งนี้ **ไม่** ขยายไปถึงเซสชัน Control UI บทบาทโหนด
    - reverse proxy แบบ loopback บนโฮสต์เดียวกันยังคงไม่ผ่านการยืนยันตัวตน trusted-proxy; ดู [การยืนยันตัวตน trusted proxy](/th/gateway/trusted-proxy-auth)

  </Accordion>
</AccordionGroup>

ดู [Tailscale](/th/gateway/tailscale) สำหรับคำแนะนำการตั้งค่า HTTPS

## นโยบายความปลอดภัยของเนื้อหา

Control UI มาพร้อมนโยบาย `img-src` ที่เข้มงวด: อนุญาตเฉพาะแอสเซ็ต **same-origin**, URL `data:` และ URL `blob:` ที่สร้างในเครื่องเท่านั้น URL รูปภาพระยะไกลแบบ `http(s)` และแบบสัมพันธ์กับโปรโตคอลจะถูกเบราว์เซอร์ปฏิเสธและจะไม่เกิดการดึงข้อมูลผ่านเครือข่าย

ในทางปฏิบัติ หมายความว่า:

- อวาตาร์และรูปภาพที่ให้บริการใต้พาธสัมพันธ์ (เช่น `/avatars/<id>`) ยังแสดงผลได้ รวมถึงเส้นทางอวาตาร์ที่ผ่านการยืนยันตัวตนซึ่ง UI ดึงมาและแปลงเป็น URL `blob:` ในเครื่อง
- URL แบบ inline `data:image/...` ยังแสดงผลได้ (มีประโยชน์สำหรับ payload ในโปรโตคอล)
- URL `blob:` ในเครื่องที่สร้างโดย Control UI ยังแสดงผลได้
- URL อวาตาร์ระยะไกลที่ปล่อยออกมาจาก metadata ของช่องทางจะถูกตัดออกที่ตัวช่วยอวาตาร์ของ Control UI และแทนที่ด้วยโลโก้/แบดจ์ในตัว ดังนั้นช่องทางที่ถูกเจาะหรือประสงค์ร้ายจึงไม่สามารถบังคับให้เบราว์เซอร์ของผู้ปฏิบัติงานดึงรูปภาพระยะไกลตามอำเภอใจได้

คุณไม่จำเป็นต้องเปลี่ยนอะไรเพื่อให้ได้พฤติกรรมนี้ เพราะเปิดใช้อยู่เสมอและกำหนดค่าไม่ได้

## การยืนยันตัวตนเส้นทางอวาตาร์

เมื่อกำหนดค่าการยืนยันตัวตน gateway แล้ว endpoint อวาตาร์ของ Control UI ต้องใช้โทเคน gateway เดียวกับ API ส่วนอื่น:

- `GET /avatar/<agentId>` ส่งคืนรูปภาพอวาตาร์เฉพาะให้ผู้เรียกที่ผ่านการยืนยันตัวตนเท่านั้น `GET /avatar/<agentId>?meta=1` ส่งคืน metadata ของอวาตาร์ภายใต้กฎเดียวกัน
- คำขอที่ไม่ผ่านการยืนยันตัวตนไปยังเส้นทางใดเส้นทางหนึ่งจะถูกปฏิเสธ (ตรงกับเส้นทาง assistant-media ที่อยู่ระดับเดียวกัน) สิ่งนี้ป้องกันไม่ให้เส้นทางอวาตาร์รั่วไหลข้อมูลประจำตัวเอเจนต์บนโฮสต์ที่ได้รับการป้องกันด้วยวิธีอื่น
- Control UI เองส่งต่อโทเคน gateway เป็นส่วนหัว bearer เมื่อดึงอวาตาร์ และใช้ URL blob ที่ผ่านการยืนยันตัวตนเพื่อให้รูปภาพยังแสดงในแดชบอร์ดได้

หากคุณปิดใช้งานการยืนยันตัวตน gateway (ไม่แนะนำบนโฮสต์ที่ใช้ร่วมกัน) เส้นทางอวาตาร์ก็จะไม่ต้องยืนยันตัวตนเช่นกัน สอดคล้องกับ gateway ส่วนอื่น

## การยืนยันตัวตนเส้นทางสื่อของผู้ช่วย

เมื่อกำหนดค่าการยืนยันตัวตน gateway แล้ว ตัวอย่างสื่อโลคัลของผู้ช่วยจะใช้เส้นทางสองขั้นตอน:

- `GET /__openclaw__/assistant-media?meta=1&source=<path>` ต้องใช้การยืนยันตัวตนผู้ปฏิบัติงาน Control UI ตามปกติ เบราว์เซอร์ส่งโทเคน gateway เป็นส่วนหัว bearer เมื่อตรวจสอบความพร้อมใช้งาน
- การตอบกลับ metadata ที่สำเร็จจะมี `mediaTicket` อายุสั้นที่ถูกจำกัดขอบเขตกับพาธต้นทางนั้นเท่านั้น
- URL รูปภาพ เสียง วิดีโอ และเอกสารที่เบราว์เซอร์แสดงผลใช้ `mediaTicket=<ticket>` แทนโทเคนหรือรหัสผ่าน gateway ที่ใช้งานอยู่ ตั๋วจะหมดอายุอย่างรวดเร็วและไม่สามารถอนุญาตต้นทางอื่นได้

สิ่งนี้ทำให้การแสดงผลสื่อตามปกติยังเข้ากันได้กับองค์ประกอบสื่อแบบ native ของเบราว์เซอร์ โดยไม่ใส่ข้อมูลรับรอง gateway ที่นำกลับมาใช้ซ้ำได้ใน URL สื่อที่มองเห็นได้

## การสร้าง UI

Gateway ให้บริการไฟล์สแตติกจาก `dist/control-ui` สร้างไฟล์เหล่านี้ด้วย:

```bash
pnpm ui:build
```

base แบบสัมบูรณ์ที่เลือกใช้ได้ (เมื่อคุณต้องการ URL แอสเซ็ตแบบคงที่):

```bash
OPENCLAW_CONTROL_UI_BASE_PATH=/openclaw/ pnpm ui:build
```

สำหรับการพัฒนาในเครื่อง (dev server แยกต่างหาก):

```bash
pnpm ui:dev
```

จากนั้นชี้ UI ไปที่ URL Gateway WS ของคุณ (เช่น `ws://127.0.0.1:18789`)

## หน้า Control UI ว่างเปล่า

หากเบราว์เซอร์โหลดแดชบอร์ดว่างและ DevTools ไม่แสดงข้อผิดพลาดที่เป็นประโยชน์ ส่วนขยายหรือ content script ช่วงต้นอาจขัดขวางไม่ให้แอปโมดูล JavaScript ประเมินผล หน้า static มีแผงกู้คืน HTML ธรรมดาที่ปรากฏเมื่อ `<openclaw-app>` ไม่ถูกลงทะเบียนหลังเริ่มต้น

ใช้การกระทำ **ลองอีกครั้ง** ของแผงหลังเปลี่ยนสภาพแวดล้อมเบราว์เซอร์ หรือโหลดใหม่ด้วยตนเองหลังตรวจสอบสิ่งเหล่านี้:

- ปิดใช้งานส่วนขยายที่แทรกเข้าไปในทุกหน้า โดยเฉพาะส่วนขยายที่มี content script แบบ `<all_urls>`
- ลองใช้หน้าต่างส่วนตัว โปรไฟล์เบราว์เซอร์ใหม่ หรือเบราว์เซอร์อื่น
- ให้ Gateway ทำงานต่อและตรวจสอบ URL แดชบอร์ดเดียวกันหลังเปลี่ยนเบราว์เซอร์

## การดีบัก/ทดสอบ: dev server + Gateway ระยะไกล

Control UI เป็นไฟล์สแตติก เป้าหมาย WebSocket สามารถกำหนดค่าได้และอาจแตกต่างจาก origin ของ HTTP วิธีนี้สะดวกเมื่อคุณต้องการ Vite dev server ในเครื่อง แต่ Gateway ทำงานอยู่ที่อื่น

<Steps>
  <Step title="Start the UI dev server">
    ```bash
    pnpm ui:dev
    ```
  </Step>
  <Step title="Open with gatewayUrl">
    ```text
    http://localhost:5173/?gatewayUrl=ws%3A%2F%2F<gateway-host>%3A18789
    ```

    การยืนยันตัวตนแบบครั้งเดียวที่เลือกใช้ได้ (หากจำเป็น):

    ```text
    http://localhost:5173/?gatewayUrl=wss%3A%2F%2F<gateway-host>%3A18789#token=<gateway-token>
    ```

  </Step>
</Steps>

<AccordionGroup>
  <Accordion title="Notes">
    - `gatewayUrl` ถูกเก็บใน localStorage หลังโหลดและถูกลบออกจาก URL
    - หากคุณส่ง endpoint `ws://` หรือ `wss://` แบบเต็มผ่าน `gatewayUrl` ให้ URL-encode ค่า `gatewayUrl` เพื่อให้เบราว์เซอร์แยกวิเคราะห์ query string ได้ถูกต้อง
    - ควรส่ง `token` ผ่าน URL fragment (`#token=...`) เมื่อเป็นไปได้ fragment จะไม่ถูกส่งไปยังเซิร์ฟเวอร์ ซึ่งหลีกเลี่ยงการรั่วไหลผ่าน request-log และ Referer พารามิเตอร์ query แบบเดิม `?token=` ยังคงถูกนำเข้าได้หนึ่งครั้งเพื่อความเข้ากันได้ แต่ใช้เป็น fallback เท่านั้น และจะถูกตัดออกทันทีหลัง bootstrap
    - `password` ถูกเก็บไว้ในหน่วยความจำเท่านั้น
    - เมื่อ `gatewayUrl` ถูกตั้งค่า UI จะไม่ fallback ไปใช้ข้อมูลรับรองจาก config หรือ environment ให้ระบุ `token` (หรือ `password`) อย่างชัดเจน การขาดข้อมูลรับรองที่ระบุอย่างชัดเจนถือเป็นข้อผิดพลาด
    - ใช้ `wss://` เมื่อ Gateway อยู่หลัง TLS (Tailscale Serve, HTTPS proxy ฯลฯ)
    - `gatewayUrl` ยอมรับเฉพาะในหน้าต่างระดับบนสุด (ไม่ใช่แบบฝัง) เพื่อป้องกัน clickjacking
    - การปรับใช้ Control UI ที่ไม่ใช่ loopback ต้องตั้งค่า `gateway.controlUi.allowedOrigins` อย่างชัดเจน (origin เต็ม) ซึ่งรวมถึงการตั้งค่า dev ระยะไกล
    - การเริ่มต้น Gateway อาจ seed origin ในเครื่อง เช่น `http://localhost:<port>` และ `http://127.0.0.1:<port>` จาก bind และ port ของ runtime ที่มีผล แต่ origin ของเบราว์เซอร์ระยะไกลยังต้องมีรายการอย่างชัดเจน
    - อย่าใช้ `gateway.controlUi.allowedOrigins: ["*"]` ยกเว้นสำหรับการทดสอบในเครื่องที่ควบคุมอย่างเข้มงวด หมายถึงอนุญาต origin ของเบราว์เซอร์ใดก็ได้ ไม่ใช่ "จับคู่กับโฮสต์อะไรก็ตามที่ฉันใช้อยู่"
    - `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true` เปิดใช้งานโหมด fallback origin จากส่วนหัว Host แต่เป็นโหมดความปลอดภัยที่อันตราย

  </Accordion>
</AccordionGroup>

ตัวอย่าง:

```json5
{
  gateway: {
    controlUi: {
      allowedOrigins: ["http://localhost:5173"],
    },
  },
}
```

รายละเอียดการตั้งค่าการเข้าถึงระยะไกล: [การเข้าถึงระยะไกล](/th/gateway/remote)

## ที่เกี่ยวข้อง

- [แดชบอร์ด](/th/web/dashboard) — แดชบอร์ด gateway
- [การตรวจสอบสถานะ](/th/gateway/health) — การตรวจสอบสถานะ gateway
- [TUI](/th/web/tui) — อินเทอร์เฟซผู้ใช้บนเทอร์มินัล
- [WebChat](/th/web/webchat) — อินเทอร์เฟซแชทบนเบราว์เซอร์
