# เซสชันที่ปิดผนึกจากต้นทางถึงปลายทาง (เซสชันที่ปิดผนึก)

> HTML: https://remote.ivjn.us/docs/th/sealed-sessions/

Android Console เปิดการเข้ารหัสจากต้นทางถึงปลายทางจากการตั้งค่า พร้อมด้วยการเลือกอุปกรณ์ภายในหน้า การตั้งค่าบัญชี Android และ Web Console รองรับการตั้งค่าหรือการเปลี่ยนรหัสผ่าน และการผูกหรือเปลี่ยนอีเมล อีเมลที่มีอยู่ยังคงใช้งานได้จนกว่าที่อยู่ใหม่จะได้รับการยืนยัน Console สำรองข้อมูลคีย์ส่วนตัว root ไปที่ Exchange ด้วยข้อความรหัสผ่านว่างตามค่าเริ่มต้น โดยคงการสำรองข้อมูลข้อความรหัสผ่านที่มีอยู่ การลบข้อมูลสำรองจะปิดใช้การบันทึกอัตโนมัติบนไคลเอนต์นั้น ข้อความรหัสผ่านที่ว่างเปล่าช่วยให้ Exchange หรือผู้ถือฐานข้อมูลสามารถถอดรหัสรูทได้ การกู้คืนด้วยตนเองจะตรวจสอบรากของพื้นที่ทำงานก่อนที่จะคงอยู่และซิงโครไนซ์เงินช่วยเหลือ

เป้าหมาย: แม้ว่าโฮสต์ Exchange จะถูกบุกรุกหรือผู้ปฏิบัติงานสงสัย แต่ ** ไม่สามารถอ่านคำสั่ง **, เอาต์พุต, ไบต์ PTY, เนื้อหาไฟล์, ภาพหน้าจอ หรือข้อมูล ubus และ ** ไม่สามารถตัดสินใจหรือปลอมแปลง** ว่าใครอาจดูหรือจัดการอุปกรณ์ได้ Exchange ทำหน้าที่เป็นรีเลย์สำหรับไซเฟอร์เท็กซ์และข้อมูลเมตาการกำหนดเส้นทางเท่านั้น

คำจำกัดความ: `crates/proto/src/seal.rs` (ประเภทสาย, กฎการอนุญาตในพื้นที่), `crates/proto/src/seal/crypto.rs` (การเข้ารหัสเซสชัน, แหวนเท่านั้น), `crates/proto/src/seal/backup.rs` (สำรองรหัสผ่าน: Argon2id + ChaCha20-Poly1305, ไม่ได้เชื่อมโยงกับ agent), `crates/agent/src/seal.rs` (ฝั่งอุปกรณ์), `crates/cli/src/seal.rs` (CLI ด้านข้าง), `shared/seal-session.js` (การใช้งานเซสชันที่แชร์โดย Web Console และ Android Console, @noble/curves · @noble/hashes · @noble/ciphers), `shared/seal-backup.js` (บนชุดเดียวกันของไลบรารีการสำรองข้อมูลรหัสผ่าน Argon2id) เวกเตอร์ทดสอบข้ามภาษา: `shared/seal-vectors.json` (สร้าง JS, Rust `javascript_vectors_interoperate` ตรวจสอบลายเซ็น/HKDF/ไซเฟอร์เท็กซ์ไบต์ที่สอดคล้องกันทีละไบต์), `shared/seal-rust-vectors.json` (การตอบสนองของอุปกรณ์ Rust, JS `shared/seal-session.test.js` จับมือและถอดรหัสเสร็จสมบูรณ์)

`seal approve` ยังคงเผยแพร่สิทธิ์โดยอัตโนมัติ การเรียก `seal grant --publish` ภายหลัง (หรือการเผยแพร่ตามค่าเริ่มต้นเมื่อเข้าสู่ระบบแล้ว) จะข้ามสิทธิ์ที่เผยแพร่แล้วและยังไม่หมดอายุ หากคีย์ผู้ออก คีย์ผู้รับ อุปกรณ์ และระดับสิทธิ์ตรงกัน โดยแจ้งว่าเผยแพร่แล้วและไม่สร้างโทเค็นใหม่ ใช้ `--renew-before DAYS` เพื่อต่ออายุสิทธิ์ที่ใกล้หมดอายุ ส่วน `--no-publish` ยังคงลงนามโทเค็นออฟไลน์ การตรวจสอบนี้ป้องกันรายการซ้ำจากการเรียกซ้ำตามลำดับ แต่ไม่ใช่ธุรกรรมระหว่างไคลเอนต์ที่ทำงานพร้อมกันและไม่ลบรายการซ้ำเดิม `seal show` จะสร้างตัวตนในเครื่องและลงทะเบียนคีย์เมื่อเข้าสู่ระบบแล้ว ควรใช้โปรไฟล์แยกสำหรับตัวตนทดลอง

## โมเดลภัยคุกคาม

| รายการ | การป้องกันเซสชันที่ปิดผนึกและขอบเขต |
|---|---|
| บรรทัดคำสั่ง stdout/stderr รหัสทางออก | **ไม่สามารถมองเห็นได้**; การตรวจสอบบันทึกเฉพาะ `sealed:true`, รหัสสาธารณะของผู้ปฏิบัติงาน, ระดับการให้สิทธิ์และการหมดอายุ และผลลัพธ์สุดท้าย |
| อินพุต PTY และเอาต์พุตหน้าจอ | **Invisible** เฟรมข้อความเข้ารหัสไม่ได้รับการตรวจสอบ |
| เนื้อหาไฟล์ ภาพหน้าจอ โครงสร้างอินเทอร์เฟซ พารามิเตอร์ ubus และผลลัพธ์ | **มองไม่เห็น** |
| ใครสามารถทำอะไรได้บ้าง (ผู้ดูแลระบบระดับสูง/ผู้ดูแลระบบ/การดำเนินงานและการบำรุงรักษา/อ่านอย่างเดียว ดูอย่างเดียว/จัดการได้) | **Device Judgement**: เฉพาะการอนุญาตที่ออกโดยคีย์สาธารณะ root ของเวิร์กสเปซคงที่ในเครื่องเท่านั้นที่ได้รับการยอมรับ Exchange ไม่สามารถออก ขยาย หรือปลอมแปลงได้ |
| อุปกรณ์แอบอ้าง | เป็นไปไม่ได้: ลายเซ็นคีย์ข้อมูลประจำตัวของอุปกรณ์ดั้งเดิม + รหัสสาธารณะของอุปกรณ์ที่ปักหมุด CLI การตรวจสอบส่วนขยายเบราว์เซอร์ พื้นที่ทำงานการพิสูจน์ห่วงโซ่รูทและลายเซ็นการตอบสนอง |
| แทรกการกดแป้นพิมพ์/คำสั่งลงในเซสชัน | เป็นไปไม่ได้: การรับรองความถูกต้อง AEAD + ตัวนับที่เพิ่มขึ้นอย่างเคร่งครัด agent ละทิ้งข้อความที่ชัดเจนที่ส่งไปยังหมายเลขเซสชันที่ปิดผนึก |
| เล่นซ้ำ เรียงลำดับใหม่ สูญเสียแพ็กเก็ต การตัดทอน | ตรวจพบและปิดเซสชัน (ปิดไม่สำเร็จ) |
| ข้อมูลเมตาที่ยังคงมองเห็นได้ | รหัสอุปกรณ์ บัญชีผู้ให้บริการ หมายเลขเซสชัน การประทับเวลา ขนาดแพ็คเก็ต และจำนวน ไม่ว่าข้อมูลจะเป็น stdout หรือ stderr หรือไม่ก็ตาม: ประเภทนั้นถูกเข้ารหัส |
| การดำเนินการยังคงเป็นไปได้ | การปฏิเสธการให้บริการ: การระงับการส่งต่อ การยกเลิกการเชื่อมต่อ หรือการปิดเซสชัน (`sealed_close`) สิ่งนี้ส่งผลต่อความพร้อมใช้งาน ไม่ใช่การอนุญาต |

อยู่นอกขอบเขตการป้องกัน: อุปกรณ์ที่ถูกบุกรุกหรือคอมพิวเตอร์ของผู้ปฏิบัติงาน หรือ Exchange ปลอมตัวเป็นอุปกรณ์ในการเชื่อมต่อครั้งแรก (TOFU) แล้ว เปรียบเทียบลายนิ้วมือนอกแบนด์เพื่อหลีกเลี่ยงกรณีสุดท้าย การทำงานของอุปกรณ์ทั้งหมดต้องมีเซสชันที่ปิดสนิท ไดเร็กทอรีคำสั่งที่ตั้งไว้ล่วงหน้าและขอบเขตการอนุญาตรูทเชนของเบราว์เซอร์มีอธิบายไว้ด้านล่างนี้

## ความครอบคลุมและการออกแบบเส้นทาง

| เส้นทาง | ลักษณะการทำงานของเซสชันที่ปิดผนึก | การอนุญาตและขอบเขตด้านอุปกรณ์ |
|---|---|---|
| HTTP `POST /api/devices/{id}/exec` | **Explicit Reject**, HTTP 409, พร้อมท์ให้ใช้เซสชัน `open_sealed` WebSocket | อุปกรณ์ยังคงปฏิเสธข้อความที่ชัดเจน `exec_req` แม้ว่า Exchange จะเพิกเฉยต่อนโยบาย |
| คำสั่ง HTTP ที่ตั้งไว้ล่วงหน้า `/commands/{cmd}/run` | ** ปฏิเสธอย่างชัดเจน ** พร้อมแจ้งให้ประทับตรา WS | คำสั่งในฐานข้อมูลเซิร์ฟเวอร์ไม่ใช่การอนุญาตฝั่งอุปกรณ์ |
| คำสั่งที่ตั้งไว้ล่วงหน้าของเว็บ Console/CLI | รับเทมเพลตจากไดเร็กทอรี ส่ง `exec_req` โดยผู้ดำเนินการภายใน WS ที่ปิดผนึก เอาต์พุตจะถูกปิดผนึกด้วย | อุปกรณ์ตรวจสอบสวัสดี การอนุญาตลายเซ็นรูท การเพิกถอน ระยะเวลาที่มีผล และระดับ `manage` `view` ไม่สามารถเรียกใช้งานเทมเพลตได้ |
| แลน `/ws/operator` | เซสชันที่ปิดผนึกเท่านั้น สวัสดี รวมกลุ่มการให้สิทธิ์และใบรับรองโดยไม่มีโทเค็นผู้ถือ | การตรวจสอบห่วงโซ่รากและดู/จัดการกฎเดียวกันกับ Exchange เลเยอร์ชั้นนอกจะตรวจสอบเฉพาะที่อยู่เครือข่ายส่วนตัวและแหล่งกำเนิด `/api/devices` ส่งคืนเฉพาะ ID อุปกรณ์และประทับตราคีย์สาธารณะ/โหมด |
| เบราว์เซอร์ Agent / เส้นทางที่ปิดผนึกดั้งเดิม | ใช้ `open_sealed` / `sealed_input` / `sealed_event` ปฏิเสธช่องเบราว์เซอร์เก่า | ขยายการอนุญาตลายเซ็นรูทของผู้ดำเนินการตรวจสอบเพิ่มเติม / สายโซ่ใบรับรอง; CLI / Console ตรวจสอบส่วนขยายรูทเชน `SealAccept.peer` และต้องการให้พับลิกคีย์เท่ากับพับลิกคีย์ลายเซ็นการตอบสนอง |
| คลี `exec-attach` / `exec-hold-tail` / `exec-hold-cat` | คำขอที่ปิดผนึกดั้งเดิม เฟรมเอาท์พุต และเหตุการณ์การเสร็จสิ้น | ต้องใช้ `manage`; งานที่ปิดผนึกไม่สามารถอ่านหรือแนบเป็นข้อความธรรมดาได้ |
| CLI `sensors --live` | ใช้เซสชันที่ปิดผนึกแยกกัน (การดำเนินการเดียว/เซสชัน) สำหรับการสุ่มตัวอย่างแต่ละรายการ Ctrl-C เพื่อสิ้นสุดการรีเฟรช | `view`; อย่าบรรจุตัวอย่างหลายตัวอย่างลงในอุโมงค์การทำงานเดียว |
| CLI `net` | ซีล `net_req` และ `net_resp` รวมถึง URL ส่วนหัว เนื้อหา ผลลัพธ์ | ต้องใช้ `manage` |
| CLI `device update` | `update_req` ปิดผนึกความคืบหน้าและสถานะสุดท้ายทั้งหมดแล้ว | ต้องใช้ `manage`; `update` ระดับบนสุดของ CLI อัปเดตไคลเอ็นต์ในเครื่อง ไม่ใช่เซสชันระยะไกล |
| exec / PTY / ไฟล์ / ubus / Android / เซ็นเซอร์เดี่ยว / พักรายการ, ฆ่า | มีการดำเนินการซีลแบบเนทีฟและยังคงไม่เปลี่ยนแปลง | ใช้ `required_level` และโปรแกรมเลือกจ่ายงานฝั่งอุปกรณ์เดียวกัน |
| รายการอุปกรณ์ เมตริก การแสดงตน รายการเซสชัน Exchange | ข้อมูลเมตาการกำหนดเส้นทาง/การตรวจสอบ ไม่ใช่การทำงานของอุปกรณ์ที่ปิดผนึก | Exchange มองเห็นได้; ไม่ได้รับอนุญาตให้เป็นการทำงานของอุปกรณ์ |

### HTTP และคำสั่งที่ตั้งไว้ล่วงหน้า

ขณะนี้การดำเนินการที่ปิดผนึกแบบเนทีฟรองรับผู้เรียก HTTP เท่านั้น ใช้ `remote-cli --transport http` หรือ `REMOTE_TRANSPORT=http` `POST /api/sealed/connections` สร้างกล่องจดหมาย `POST /input` ส่งการจับมือหรือไซเฟอร์เท็กซ์ที่ลงนามแล้ว ผลการสำรวจความคิดเห็น `GET /events` และ `DELETE` ปิดการเชื่อมต่อ ลูกค้ายังคงรักษาการเข้ารหัสแบบ end-to-end และการอนุญาตอุปกรณ์ที่มีอยู่ จุดสิ้นสุดการดำเนินการของอุปกรณ์เดิมยังคงส่งคืน 409 การเชื่อมต่ออุปกรณ์ยังคงไม่เปลี่ยนแปลง

https://remote.ivjn.us/sealed-http.md


การสร้าง การแก้ไข และการแสดงรายการคำสั่งที่ตั้งไว้ล่วงหน้ายังคงเป็นพื้นที่การจัดการฐานข้อมูล Exchange และข้อความเทมเพลต ชื่อ และคำอธิบายในไดเร็กทอรีจะไม่ถูกปิดผนึกข้อมูล และคำสั่งที่ลงนามโดยรายการอนุญาตรูท คอนโซลและ CLI เรียกใช้งานเทมเพลตที่ดึงข้อมูลมาในฐานะผู้บริหารที่ปิดผนึกตามปกติ (ไม่ระงับ) ในขณะที่กดปุ่มค้างไว้ อุปกรณ์เชื่อถือเฉพาะนโยบายดั้งเดิมและการอนุญาตของผู้ปฏิบัติงาน ไม่ใช่รหัสไดเรกทอรี บทบาทของเซิร์ฟเวอร์ หรืออาร์กิวเมนต์ "นี่คือคำสั่งที่กำหนดไว้ล่วงหน้า" สมาชิกที่ถือเฉพาะการให้สิทธิ์ `view` ไม่สามารถรันคำสั่งที่กำหนดไว้ล่วงหน้าได้ จำเป็นต้องมีการให้สิทธิ์ root `manage` Grant ไม่สามารถขยายได้หากไดเร็กทอรีถูกดัดแปลง แต่ `manage` อนุญาตให้ดำเนินการได้ตามต้องการ ดังนั้นโปรดตรวจสอบเทมเพลตและอย่าเขียนรหัสผ่าน/โทเค็นลงในไดเร็กทอรีเซิร์ฟเวอร์ เอาต์พุตจะถูกถอดรหัสและแสดงเฉพาะบนเครื่องเท่านั้น การตรวจสอบรันพาธจะบันทึกเฉพาะเมตาดาต้าที่ปิดผนึกเท่านั้น และไม่บันทึกคำสั่งและเอาต์พุต

### การเลือก LAN และไคลเอนต์

เซสชันอุปกรณ์ CLI ใช้เฉพาะการปิดผนึกแบบ end-to-end เท่านั้น โดยไม่ขึ้นกับ `seal_key` ความสามารถ หรือคำแนะนำนโยบายที่รายงานโดย Exchange ข้อมูลความสามารถที่ขาดหายไปหรือถูกลบจะไม่ทำให้มีการปรับลดรุ่นข้อความธรรมดา ความล้มเหลวในการอ่านฐานข้อมูลพินในเครื่องถือเป็นข้อผิดพลาด บันทึกความน่าเชื่อถือที่เสียหายจะไม่ถูกละเลย

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

LAN นำซองจดหมายดั้งเดิม การถอดเสียงลายเซ็น ตัวนับ AEAD การแก้ไขอุปกรณ์ และกฎการเพิกถอนมาใช้ซ้ำ โดยไม่ต้องสร้างอัลกอริธึมการเข้ารหัสใหม่ LAN ขาออกต้องผ่าน `Tunnels::seal_outbound` และแปลเป็น `OperatorDown`; การแทรกข้อความธรรมดาของหมายเลขเซสชันที่ปิดผนึกจะถูกยกเลิกโดยผู้จ่ายงานอุปกรณ์ การเชื่อมต่อถูกปิดในขณะที่ยังคงปลดผู้บริหารที่ถือไว้ ยกเลิกการดำเนินการอื่นๆ และเอาต์พุตล่าช้าจะถูกละทิ้งโดย ID ที่ไม่พอใจ ไม่มีโทเค็น LAN: ไคลเอ็นต์ใส่การอนุญาตแคชแบบออฟไลน์และห่วงโซ่ใบรับรองลงในสวัสดี และอุปกรณ์จะกำหนดมุมมอง/จัดการตาม `authorize` เดียวกันกับ Exchange เลเยอร์ชั้นนอกจะตรวจสอบเฉพาะที่อยู่เครือข่ายส่วนตัวและแหล่งกำเนิดเท่านั้น เซสชันข้อความที่ชัดเจนจะถูกปฏิเสธบน LAN เสมอ โดยไม่คำนึงถึงค่านโยบายในอดีต เว็บคอนโซลที่โฮสต์ยังคงเชื่อมต่อกับ Exchange

ข้อมูลอุปกรณ์เสริม `info.seal_mode` (`allow` / `require`), `seal_key` และความสามารถของ `sealed` ยังคงเข้ากันได้ ไม่ใช่ทั้งการอนุญาตหรือกุญแจสาธารณะของอุปกรณ์ที่เชื่อถือได้ ในที่สุดอุปกรณ์ก็บังคับใช้นโยบาย คำสั่ง เทอร์มินัล ไฟล์ ubus เซ็นเซอร์ งาน โพรบเครือข่าย การทำงานของเดสก์ท็อป การอัปเดตอุปกรณ์ คำสั่งที่กำหนดไว้ล่วงหน้า และการทำงานของเบราว์เซอร์ใน CLI, เว็บ Console และ Android Console ล้วนใช้เซสชันที่ปิดผนึกแบบเนทีฟ ไม่มีเส้นทางข้อความธรรมดา ค่านโยบาย `allow` ยังปฏิเสธการดำเนินการกับข้อความธรรมดาอีกด้วย ความล้มเหลวของการจับมือ การปักหมุดอุปกรณ์ การให้สิทธิ์ หรือการถอดรหัสจะหยุดการดำเนินการโดยไม่ต้องลองข้อความธรรมดาอีกครั้ง การเปลี่ยนแปลงนโยบายออนไลน์รีเฟรชข้อมูล LAN ทันที ข้อมูลคลาวด์จะรีเฟรชเมื่อเชื่อมต่อใหม่และลงทะเบียน

### การตรวจสอบสิทธิ์รูทเชนพื้นที่ทำงานของเบราว์เซอร์ Agent

ส่วนขยายโฆษณา `sealed`, Ed25519 `seal_key` และ `seal_mode: require` หลังจากจับคู่หรือกำหนดค่าอุปกรณ์แล้ว ระบบจะเรียก `POST /api/seal/device` พร้อมข้อมูลรับรองอุปกรณ์และหลักฐานการครอบครองที่ลงนาม Exchange รับพื้นที่ทำงานจากอุปกรณ์และผูกพับลิกคีย์ของอุปกรณ์ ไคลเอนต์ที่ถือคีย์ส่วนตัวรูทของพื้นที่ทำงานที่เชื่อถือได้ลงนามในสิทธิ์การดูสำหรับอุปกรณ์เบราว์เซอร์นั้น ๆ การลงทะเบียนและผู้ถืออุปกรณ์ให้การเข้าถึงรีเลย์และการดึงข้อมูลรับรอง เซสชันเบราว์เซอร์ต้องมีการรับรองความถูกต้องของใบรับรอง

ส่วนขยายจะตรวจสอบลายเซ็นขาเข้า ห่วงโซ่รูท ขอบเขตอุปกรณ์ ระดับ การหมดอายุ และการเพิกถอนในเครื่อง จากนั้นลงนาม `SealAccept` ด้วย `SealHello` ที่ลงนามของตนเองใน `peer` CLI `finish_workspace_operator` และ `finishBrowser` ที่แชร์จะตรวจสอบการพิสูจน์นั้น ต้องใช้ `peer.operator_key == accept.device_key` และตรวจสอบลายเซ็นที่ยอมรับ การพิสูจน์ที่ขาดหายไปหรือไม่ถูกต้องจะปิดเซสชัน เซสชันที่ถูกต้องจะเริ่มต้นโดยอัตโนมัติ Raw CDP ยังต้องการความยินยอมในท้องถิ่นตามคำขอหรือการอนุมัติแบบอัตโนมัติซึ่งกำหนดขอบเขตไว้ที่คีย์ผู้ปฏิบัติงาน เซิร์ฟเวอร์ และอุปกรณ์ สิทธิ์การดูไม่สามารถใช้ CDP ได้

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

**ขอบเขตความปลอดภัย:** TOFU รูทแรกไม่สามารถแยกแยะรูทปลอมที่มาจากรีเลย์ที่เป็นอันตราย ตรวจสอบมันออกจากวง PoP พิสูจน์การครอบครองคีย์ ไม่ใช่การอนุมัติจากสมาชิก การอนุมัติอัตโนมัติของไคลเอนต์รูทขึ้นอยู่กับบัญชีรายชื่อ การใช้งานที่มีความละเอียดอ่อนควรตรวจสอบคีย์ใหม่ ปกป้องโปรไฟล์ส่วนขยาย เมล็ดพันธุ์ และห่วงโซ่การเผยแพร่คอนโซลเว็บ จุดสิ้นสุดที่ถูกบุกรุกหรือการเข้าถึง CDP เต็มรูปแบบที่มอบให้แก่ผู้ปฏิบัติงานที่เป็นอันตรายนั้นอยู่นอกเหนือการป้องกันของการปิดผนึก การลบบันทึกไดเรกทอรีไม่เพิกถอนข้อมูลรับรองที่ลงนามในแคช กฎการเพิกถอนภายในอุปกรณ์และการหมดอายุยังคงมีความสำคัญ Exchange ยังสามารถสังเกตข้อมูลเมตาการกำหนดเส้นทาง ระงับข้อมูลรับรอง และปฏิเสธบริการ

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

กล่องจดหมาย HTTP จำเป็นต้องมีความสัมพันธ์กับการสร้างอินสแตนซ์ Exchange และหมดอายุหลังจาก 90 วินาทีโดยไม่มีกิจกรรม ทำให้อินพุตเป็นอนุกรม ลองเฉพาะลำดับและข้อความไซเฟอร์เดียวกันอีกครั้ง และรับทราบเหตุการณ์ที่ประมวลผลด้วย `after` เว็บ Console, อุปกรณ์เคลื่อนที่ และ LAN ยังคงรักษาการขนส่งที่มีอยู่ การดำเนินการที่กำหนดไว้ล่วงหน้ายังคงต้องใช้ `manage`

### การตรวจสอบการถดถอย

การทดสอบ Rust ครอบคลุมการจับมือที่อนุญาตโดยอุปกรณ์จริง, ผลลัพธ์ของ exec/attach/tail/cat/sensors/net/update ที่ปิดผนึก, การปฏิเสธข้อความธรรมดาภายใต้ทุกค่านโยบาย, LAN WebSocket การจับมือ/เฟรม/การอัปเดตนโยบายที่ปิดผนึก, คำแนะนำ HTTP 409, การปิดเส้นทางโหมดต้องใช้เบราว์เซอร์ และค่าคงที่การปิดผนึกที่บังคับสำหรับไคลเอนต์และข้อมูลอุปกรณ์ การทดสอบ JavaScript ครอบคลุมเวกเตอร์ Rust/JS แบบสองทิศทางที่มีอยู่ ซองจดหมายการดำเนินการ เอาต์พุตคำสั่งที่กำหนดไว้ล่วงหน้าแบบปิดผนึกโดยไม่มีทางเลือกอื่นหลังจากการปฏิเสธ การกำหนดค่าเบราว์เซอร์/การลงทะเบียน/การปฏิเสธอุปกรณ์/การเปลี่ยนแปลงนโยบายท้องถิ่น และข้อผิดพลาดในการปักหมุดไคลเอ็นต์ ซิงเกิลตันการปิดผนึกทั้งกระบวนการจะเริ่มต้นในกระบวนการย่อยที่แยกได้ในระหว่างการทดสอบ ดังนั้นการทดสอบที่เกิดขึ้นพร้อมกันจึงไม่สามารถแก้ไขนโยบายด้านอุปกรณ์ของกันและกันได้

```sh
CARGO_PROFILE_DEV_DEBUG=0 CARGO_PROFILE_TEST_DEBUG=0 cargo test --workspace
cargo fmt --all --check
CARGO_PROFILE_DEV_DEBUG=0 CARGO_PROFILE_TEST_DEBUG=0 cargo clippy --workspace --all-targets -- -D warnings
npm test --prefix shared
npm test --prefix website/console
npm test --prefix browser-agent
node --test scripts/test-pages-worker.mjs
```

จาวาสคริปต์ใช้โหนด 24+ เพิ่มการถดถอย LAN แบบปิดผนึกสำหรับการอัปเกรด HTTP จริง / WebSocket ผ่านการสตรีมดูเพล็กซ์ในหน่วยความจำ พร้อมความสามารถในการตรวจสอบการจับมือกัน เฟรม และการปิดผนึกบังคับภายใต้ทุกค่านโยบายในแซนด์บ็อกซ์ที่ปิดใช้งานซ็อกเก็ตเครือข่าย การทดสอบ WS/TLS/HTTP แบบวนกลับที่มีอยู่ยังคงต้องอยู่ในสภาพแวดล้อมที่อนุญาตให้รวมพอร์ตได้ มิฉะนั้นจะล้มเหลวด้วย `Operation not permitted` และไม่สามารถใช้เป็นหลักฐานว่าชุดการทดสอบทั้งหมดผ่านการทดสอบ การตรวจสอบเครือข่ายและการทดสอบเส้นทางการอัปเดตจะตรวจสอบผลลัพธ์ความสำเร็จ/ข้อผิดพลาดของการปิดผนึก การอัปเดตจะไม่ถูกดาวน์โหลดหรือติดตั้ง

** ความเสี่ยงพิเศษของเว็บ Console:** รหัสของหน้าเว็บนั้นถูกส่งโดยไซต์ Pages ไซต์/CDN ที่ถูกบุกรุกสามารถออกสคริปต์ที่เป็นอันตรายเพื่ออ่านข้อความธรรมดาของหน้านี้หรือเซ็นชื่อด้วยคีย์ของเบราว์เซอร์ (แต่ไม่สามารถนำคีย์ส่วนตัวที่ไม่สามารถส่งออกออกไปได้) ข้อกำหนดเบื้องต้นสำหรับการรักษาความลับจากตัวดำเนินการ Exchange คือคุณเชื่อถือห่วงโซ่การเผยแพร่ของไซต์แบบคงที่ หากคุณไม่แน่ใจเกี่ยวกับเรื่องนี้ โปรดใช้ CLI หรือ Android Console

## ตัวตนและกุญแจ

| คีย์ | ประเภท | ที่เก็บของ | ใครสามารถดูกุญแจสาธารณะได้ |
|---|---|---|---|
| ข้อมูลระบุตัวตนของอุปกรณ์ | เอ็ด25519 | agent: `agent.seal.key` (0600) เมื่อไฟล์สถานะอยู่บน tmpfs (`/var/run`, `/run`, `/tmp`) พื้นที่เก็บข้อมูลจะย้ายไปที่ `/etc/remote-agent/` แทนที่ด้วย `REMOTE_SEAL_DIR` OpenWrt รวมไว้ใน Conffiles | รายงานเป็น `info.seal_key` ระหว่างการลงทะเบียน (แสดงเท่านั้น CLI ไม่เชื่อสำเนานี้) |
| ตัวตนของผู้ปฏิบัติงาน | เอ็ด25519 | CLI: `seal-identity.pk8` (0600) ข้าง `config.toml`; เว็บ Console: ** ไม่สามารถส่งออกได้** WebCrypto Ed25519 `CryptoKeyPair` ใน IndexedDB `remote-seal` (ถอยกลับไปเป็นเมล็ดสุ่มใน IndexedDB หากเบราว์เซอร์ขาดการสนับสนุน WebCrypto Ed25519); Android Console: เมล็ดใน `expo-secure-store` (เข้ารหัสโดยที่เก็บคีย์ Android) | ส่งไประหว่างการจับมือกัน |
| รากของพื้นที่ทำงาน | เอ็ด25519 | คือคีย์ตัวดำเนินการบนไคลเอ็นต์ผู้ดูแลระบบขั้นสูง เมล็ดต้องสามารถส่งออกได้บนเว็บ Console (ผู้ดูแลระบบระดับสูงจะแทนที่คีย์ WebCrypto ที่ไม่สามารถส่งออกได้ด้วยเมล็ดในเครื่องเมื่อสร้างรูท) มันสามารถส่งออกเป็นข้อความ `rsk1.` สำรองข้อมูลไปที่ Exchange โดย Console ด้วยข้อความรหัสผ่านว่างเปล่าตามค่าเริ่มต้น | บันทึกรากของ `rsr1.` (เผยแพร่ Exchange, แก้ไขอุปกรณ์แล้ว) |
| คีย์เซสชัน | คีย์ X25519 ชั่วคราว → HKDF-SHA256 → คีย์ ChaCha20-Poly1305 หนึ่งคีย์ต่อทิศทาง | หน่วยความจำเท่านั้น ละทิ้งเมื่อเซสชันสิ้นสุดลง (ส่งต่อความลับ) | ไม่มีใคร |

คีย์ส่วนตัวของตัวดำเนินการจะถูกจัดเก็บไว้ในเครื่อง Console อัปโหลดการสำรองข้อมูลรูทด้วยข้อความรหัสผ่านว่างเปล่าตามค่าเริ่มต้น Exchange หรือผู้ถือฐานข้อมูลสามารถถอดรหัสได้ ตั้งค่าข้อความรหัสผ่านสำรองด้วยตนเอง หรือลบข้อมูลสำรองเพื่อปิดใช้งานการบันทึกอัตโนมัติบนไคลเอนต์นี้

## การอนุญาต (Exchange ไม่ใช่การอนุญาต)

อุปกรณ์จะเชื่อถือคีย์สาธารณะรูทของพื้นที่ทำงานเพียงคีย์เดียวเท่านั้น นโยบายท้องถิ่น `agent.seal.json`:

```json
{"mode":"require","root_key":"<base64 Ed25519>","revoked_keys":[]}
```

agent กำหนดระดับสำหรับ `sealed_open` แต่ละตัวด้วยตัวมันเอง:

1. ไม่มีการปักหมุดรูทพื้นที่ทำงานบนอุปกรณ์ → ปฏิเสธ (`no workspace root pinned on this device`)
2. รหัสสาธารณะของผู้ดำเนินการที่ `revoked_keys` → ถูกปฏิเสธ
3. รหัสสาธารณะของผู้ดำเนินการคือ `root_key` → `owner` (จัดการได้ + แก้ไขนโยบายท้องถิ่น)
4. มีการจัดหา `grant` มาให้: ลายเซ็นถูกต้อง, `operator_key` ตรงกับการจับมือ, `device_id` ระบุอุปกรณ์นี้หรือ `*` และสิทธิ์นั้นอยู่ภายในระยะเวลาที่มีผลใช้ได้ (±5 นาทีของการเอียงของนาฬิกา) ผู้ลงนามจะต้องเป็นรูทที่ปักหมุดไว้ หรือสวัสดีต้องมีห่วงโซ่ใบรับรอง `rsc1.` (`hello.chain` ซึ่งไม่ครอบคลุมอยู่ในลายเซ็นแฮนด์เชค) โดยเรียงลำดับจากใบรับรองที่ออกโดยรูทไปยังผู้ลงนามการให้สิทธิ์ ใบรับรองแต่ละใบจะต้องอนุญาตให้ออกเพิ่มเติมได้ และความยาวเส้นทาง ระดับ ขอบเขตอุปกรณ์ และการหมดอายุจะต้องอยู่ภายในใบรับรองก่อนหน้า ระดับสิทธิ์คือ `view` หรือ `manage` ไม่ใช่ `owner`
5. อื่นๆ → ปฏิเสธ

## ข้อจำกัดของห่วงโซ่ใบรับรอง

อุปกรณ์ยอมรับสายโซ่ `rsc1.` ที่ยืนยันกลับไปยังรูทพื้นที่ทำงานที่ปักหมุดไว้ ไม่ใช่ใบรับรองระดับกลางที่แยกออกมา (`hello.chain` สั่งจากใบรับรองที่ออกรูทไปยังผู้ลงนามการให้สิทธิ์) มี **ไม่มีการจำกัดความลึกขององค์กร**: องค์กรอาจมีมากกว่า 10 ระดับ ราวกั้น DoS ขนาดใหญ่จะจำกัดเชนไว้ที่ใบรับรอง `MAX_CERT_CHAIN` = **64** และขนาดที่เข้ารหัส (ผลรวมของความยาวโทเค็น Canonical `rsc1.`) ถึง `MAX_CERT_CHAIN_BYTES` ≤ **64 KiB** (`check_chain_limits` / `checkChainLimits` ใน `crates/proto/src/seal.rs` / `shared/seal-cert.js`; กฎที่เหมือนกันบนอุปกรณ์, Exchange, CLI, เว็บ และ Android) เกินขีดจำกัดอย่างใดอย่างหนึ่งทำให้เกิดการปฏิเสธทันที

ใบรับรองแต่ละใบยังมี `max_path_len` (0–255) ซึ่งเป็นข้อจำกัดของผู้ออก **เป็นทางเลือก** เกี่ยวกับจำนวนใบรับรองที่อาจตามมา หากไม่มี `--max-path`, `seal cert issue` จะใช้ค่าเริ่มต้นเป็นความลึกของผู้ออกที่เหลือทั้งหมด (255 ไม่จำกัดอย่างมีประสิทธิภาพ) การยืนยันกำหนดให้จำนวนใบรับรองที่ตามมาต้องไม่มากกว่า `max_path_len` ใบรับรองระดับกลางที่ออกโดยรูทให้กับผู้ดูแลระบบมี `max_path_len` 0: ผู้ดูแลระบบอาจลงนามในการอนุญาต แต่ไม่สามารถออกใบรับรองอื่นได้ `can_issue` ที่เป็นเท็จยังป้องกันการออกใบรับรองเพิ่มเติมอีกด้วย

ใบรับรองย่อยจะต้องอยู่ในใบรับรองหลัก (`cert_within_parent` / `verify_chain` บนอุปกรณ์):

- **level** ไม่เกินระดับพาเรนต์ รูตคือ `owner`; ระดับที่ได้รับต้องเป็น `view` หรือ `manage` เท่านั้น และต้องไม่สูงกว่าใบรับรองหลัก `owner` ไม่สามารถปรากฏในใบรับรองหรือสิทธิ์ได้
- **Device scope** เป็นเซ็ตย่อยของพาเรนต์ ลูกสามารถครอบคลุมอุปกรณ์ทั้งหมดได้ก็ต่อเมื่อพาเรนต์มี `*`; ไม่เช่นนั้นอุปกรณ์แต่ละชิ้นของเด็กจะต้องอยู่ในรายการของผู้ปกครอง รายการย่อยจะถูกเขียนว่า `*` เฉพาะในกรณีที่รายการหลักเป็น `*` ด้วย
- **Eexpiration time** ไม่สามารถอยู่หลังใบรับรองหลักได้ รากไม่มีขีดจำกัดการหมดอายุ นอกจากนี้ Grant ยังต้องไม่ช้ากว่าใบรับรองที่ออก และอุปกรณ์ก็ไม่สามารถเกินขอบเขตของใบรับรองนั้นได้

**การเพิกถอนใบรับรองระดับกลางจะเรียงซ้อนกับทุกสิ่งที่อยู่ด้านล่าง ** อุปกรณ์จะตรวจสอบทุก `subject_key` และ `serial` บนเชนกับ `revoked_keys` ในนโยบาย หลังจากที่อันกลางถูกเพิกถอน เชนอีกต่อไปที่ยังคงแบกมันไว้จะล้มเหลว ดังนั้นใบรับรองรองที่ออกให้ เช่นเดียวกับหน่วยงานที่ออกโดยใบรับรองเหล่านั้น จะกลายเป็นโมฆะ การลบใบรับรองจาก Exchange จะหยุดการแจกจ่ายเพิ่มเติมเท่านั้น และสำเนาที่เก็บไว้ในอุปกรณ์นั้นยังคงใช้ได้ก่อนหมดอายุ หากต้องการทำให้เชนด้านล่างทั้งหมดไม่ถูกต้องทันที ให้เขียนคีย์สาธารณะของหัวเรื่อง (หรือหมายเลขซีเรียล) ของใบรับรองลงในนโยบายด้านอุปกรณ์ `revoked_keys`

การแมประดับกับการดำเนินการ (`proto::seal::required_level`): `view` (ดูอย่างเดียว) อนุญาตให้เซ็นเซอร์ เก็บรายการงานไว้ การดำเนินการอ่านอย่างเดียว Android (สถานะ/ต้นไม้/ภาพหน้าจอ/find_text); สิ่งอื่นๆ ทั้งหมด (exec, PTY, ไฟล์, ubus, การตรวจจับเครือข่าย, การอัปเดตตัวเอง, อินพุต Android) ต้องใช้ `manage`

การให้สิทธิ์เป็น JSON ที่ลงนามโดยรูท การดัดแปลงฟิลด์ใดๆ โดย Exchange (เช่น ดู → จัดการ การเปลี่ยนอุปกรณ์ การขยายระยะเวลาที่มีผล การเปลี่ยนบุคคลที่ได้รับอนุญาต) จะทำให้ลายเซ็นเป็นโมฆะ ลายเซ็นการจับมือยังผูกการอนุญาตในเซสชันนี้ด้วย บทบาทของบัญชีในฝั่ง Exchange จะกำหนดว่า "สามารถกำหนดเส้นทางข้อความไปยังอุปกรณ์นี้ได้หรือไม่" (การเปิดเผยผู้เช่า) และจะไม่ขยายหรือลดสิทธิ์ของเซสชันที่ปิดผนึก ไม่มีการอ้างสิทธิ์ในระดับอุปกรณ์ เจ้าของ หรือเจ้าของร่วม: `owner` เดียวเท่านั้นที่เป็นรูท

**อุปกรณ์ต่างๆ ปักหมุดรูทอย่างไร:**

- **TOFU:** เมื่ออุปกรณ์ที่สามารถปิดผนึกเชื่อมต่อกับ Exchange, Exchange จะส่ง `seal_root {token, rotation?}` (บันทึก root ของ `rsr1.` ที่ลงนามด้วยตนเอง) อุปกรณ์จะตรวจสอบลายเซ็นตนเอง ถ้าไม่มีการปักหมุดรูทในเครื่อง มันจะปักหมุดรูทนี้และเขียนไปที่ `agent.seal.json`
- **Rotation:** หลังจาก root ได้รับการแก้ไขแล้ว root ที่ประกาศโดย Exchange จะเปลี่ยนไป อุปกรณ์จะเปลี่ยนเป็นรูทใหม่เมื่อได้รับบันทึกการหมุน `rsx1.` พร้อมกันซึ่งลงนามโดยรูทคงที่ (`old_root` = รูทคงที่, `new_root` = รูทใหม่ พื้นที่ทำงานเดียวกัน); มิฉะนั้นจะถูกละเว้นและบันทึกไว้ รากยังสามารถเปลี่ยนแปลงได้ในเซสชันที่ปิดผนึกด้วย `seal_policy_set {root_key}` (เฉพาะรากปัจจุบันเท่านั้นที่มีระดับ `owner`)
- **การตั้งค่าท้องถิ่น:** `remote-agent seal root <key>` ถูกเขียนโดยตรงไปยังรูท; `remote-agent seal forget-root` ถูกล้าง และ TOFU จะถูกทำซ้ำในครั้งถัดไปที่เชื่อมต่อ Exchange (การรีสตาร์ท agent จะมีผล)

**Risk:** หาก Exchange ถูกโจมตีในการเชื่อมต่อครั้งแรกของอุปกรณ์ TOFU จะปล่อยให้อุปกรณ์ทำการรูทปลอม หากเป็นข้อกังวล ให้ปักหมุดคีย์สาธารณะรูทด้วย `remote-agent seal root <key>` ก่อนที่จะนำอุปกรณ์ออนไลน์ (ดู `remote-cli seal root` สำหรับลายนิ้วมือของอุปกรณ์) เมื่อปักหมุดแล้ว Exchange จะไม่สามารถแทนที่รูทนั้นได้

**Revocation:** Root `remote-cli seal policy <设备> --revoke <key>` หรือบนอุปกรณ์ `remote-agent seal revoke <key>` ไม่สามารถเพิกถอนรากได้ การอนุญาตมีระยะเวลาที่ถูกต้อง (90 วันตามค่าเริ่มต้น) การลบทุนที่เผยแพร่ออกจาก Exchange จะไม่เป็นการแจกจ่ายอีกต่อไป สำเนาที่นำเข้าในเครื่องยังคงใช้ได้จนกว่าจะหมดอายุและต้องถูกเพิกถอนตามนโยบายด้านอุปกรณ์

## Exchange เป็นรีเลย์การให้สิทธิ์ที่ไม่น่าเชื่อถือ

เพื่อนร่วมงานไม่จำเป็นต้องคัดลอกโทเค็น `rsg1.` หรือคีย์สาธารณะลงในหน้าต่างแชทอีกต่อไป: Exchange มี **Operator Public Key Directory** และ **grant Relay** ระดับพื้นที่ทำงาน สิ่งนี้ไม่เปลี่ยนรูปแบบภัยคุกคาม - Exchange ยังคงไม่สามารถมองเห็นข้อความที่ชัดเจน และไม่สามารถออก ขยาย หรือปลอมแปลงการอนุญาตได้ มันจะช่วยประหยัด:

1. ผู้ดำเนินการใช้รหัสส่วนตัวในเครื่องเพื่อพิสูจน์รหัสสาธารณะ Ed25519 (`POST /api/seal/keys`) ที่เขาถืออยู่
2. ข้อความต้นฉบับของโทเค็น `rsg1.` ที่ลงนามแล้ว (`POST /api/seal/grants`)
3. ข้อความต้นฉบับของใบรับรองระดับกลาง `rsc1.` ที่ลงนามแล้ว (`POST /api/seal/certs`) Exchange ตรวจสอบลายเซ็น Ed25519 ของผู้ออก แต่ไม่สามารถปลอมแปลงใบรับรองได้ `GET /api/seal/certs/chain?subject_key=` ดึงโทเค็นตามความสัมพันธ์การออก (จำกัดด้วยโทเค็น 64 เดียวกัน / รั้ว 64 KiB) ออกรูตก่อน

การให้สิทธิ์อุปกรณ์ยังคงรับรู้เฉพาะรูทแบบคงที่ หรือสายใบรับรอง `rsc1.` ที่สามารถตรวจสอบรูทได้ Exchange แยกวิเคราะห์โทเค็นเพื่อสร้างดัชนีตามเวลา `owner_key` / `operator_key` / เวลาหมดอายุเท่านั้น และไม่ถือว่าการอัปโหลดเป็นการอนุญาต กรณีที่แย่ที่สุดคือผู้ดำเนินการระงับโทเค็น (อีกฝ่ายไม่สามารถรับสิทธิ์ได้ ซึ่งปรากฏเป็น `no grant`) และไม่สามารถเปลี่ยน `view` เป็น `manage` ได้ และไม่สามารถหลอกว่าเป็น root ได้

สมาชิกพื้นที่ทำงานทุกคนสามารถอัปโหลดทุนได้: โทเค็นมาพร้อมกับลายเซ็นของตัวเอง และการอัปโหลดไม่ใช่การดำเนินการอนุญาต เฉพาะสิทธิ์ที่ลงนามโดยรูทเท่านั้นที่ถูกต้องสำหรับอุปกรณ์ CLI มีค่าเริ่มต้นเป็น `--publish` เมื่อเข้าสู่ระบบ คอนโซล Web/Android ใช้อินเทอร์เฟซเดียวกันเมื่อ "ลงนามและเผยแพร่"

การผูกมัดหลักฐานการครอบครองสำหรับการลงทะเบียนกุญแจ:

```text
remote-seal-key-register:v1:<workspace_id>:<operator_id>:<public_key>:<issued_at_ms>
```

หลักฐานเป็นลายเซ็น Ed25519 ที่สร้างด้วยคีย์ส่วนตัวที่สอดคล้องกับคีย์สาธารณะนี้ `issued_at_ms` ต้องอยู่ภายใน ±10 นาทีของเวลาเซิร์ฟเวอร์ CLI พยายามลงทะเบียนบน `login` และ `seal show` คอนโซลลงทะเบียนหลังจากเข้าสู่ระบบ

หากไม่มีเงินอุดหนุนที่ตรงกันในพื้นที่ก่อนการดำเนินการปิดผนึก ผู้ปฏิบัติงานจะพยายามอย่างดีที่สุดกับ `seal sync` เมื่อการจับมือถูกปฏิเสธเนื่องจาก `no grant` มันจะซิงโครไนซ์อีกครั้งและลองอีกครั้ง

เมื่อลูกค้าของผู้ออกออนไลน์ ลูกค้าจะต่ออายุการอนุญาตที่ออกให้โดยอัตโนมัติและกำลังจะหมดอายุ ลายเซ็นยังคงเสร็จสิ้นภายในเครื่อง Exchange บันทึกเฉพาะโทเค็น `rsg1.` ใหม่และลบบันทึกเก่า และไม่สามารถเปลี่ยนระดับหรือเปลี่ยนบุคคลที่ได้รับอนุญาต

- ** เมื่อรัน: ** CLI หลังจากการเข้าสู่ระบบสำเร็จ และเมื่อคำสั่งการเข้าสู่ระบบใด ๆ เริ่มทำงาน (`seal renew` ใช้เส้นทางแบบแมนนวลและไม่ใช้เลเยอร์ของการต่ออายุอัตโนมัติ) เว็บ Console จะเข้าสู่พื้นที่ทำงานเมื่อเข้าสู่ระบบและเข้าสู่พื้นที่ทำงาน (หากมีเซสชันอยู่แล้วหลังจากโหลดเพจแล้ว) Android Console หลังจากเข้าสู่ระบบ และเมื่อแอปกลับมาทำงานเบื้องหน้า หากคุณไม่ได้เข้าสู่ระบบ หรือหากคีย์สาธารณะนี้ไม่มีการอนุญาตที่เผยแพร่ ก็จะไม่มีการต่ออายุ
- **กฎการต่ออายุ:** `GET /api/seal/grants?owner_key=<本机公钥>` เงินช่วยเหลือที่หมดอายุภายใน 30 วันจะถูกออกใหม่ด้วย `device_id`, `operator_key` และ `level` ดั้งเดิมเป็นเวลา 90 วัน หลังจากรีลีสสำเร็จ สิทธิ์เก่าจะเป็น `DELETE` หาก `GET /api/seal/keys` สำเร็จ `operator_key` ที่ไม่ได้อยู่ในไดเร็กทอรีคีย์สาธารณะอีกต่อไปจะถูกข้ามไป หากการร้องขอไดเร็กทอรีล้มเหลว มันจะไม่ถูกกรองตามไดเร็กทอรี
- **Throttling:** Web/Android: รหัสสาธารณะที่ออกเดียวกันนี้จะถูกลองใช้มากที่สุดทุกๆ 12 ชั่วโมง CLI ถูกบันทึกในไดเร็กทอรีการกำหนดค่าเป็น `seal-renew-state.json`, Web Console ใช้ `localStorage` และ Android Console ใช้ SecureStore ความล้มเหลวไม่ส่งผลต่อการดำเนินการปัจจุบัน: CLI พิมพ์เฉพาะบรรทัดคำเตือนบน stderr และคอนโซลใช้ `console.warn` เมื่อการต่ออายุสำเร็จ CLI จะพิมพ์คำอธิบายสั้น ๆ เกี่ยวกับ stderr เว็บ Console ที่เก็บรูทจะออกหนึ่งรอบก่อนเมื่อเข้าสู่พื้นที่ทำงาน และ Android จะออกหนึ่งรอบก่อนเมื่อเปิดใช้งาน "การเข้ารหัสจากต้นทางถึงปลายทาง" สองครั้งนี้ไม่อยู่ภายใต้การควบคุมปริมาณ 12 ชั่วโมง การรีเฟรชหลังจากการเข้าพักครั้งเดียวกันเท่านั้นที่จะถูกควบคุมปริมาณ
- **การต่ออายุ CLI:** บันทึกความสำเร็จแยกตามเซิร์ฟเวอร์และกุญแจผู้ออก หลังสำเร็จจะข้ามคำขอต่ออายุเป็นเวลา 12 ชั่วโมง หากล้มเหลวหรือถูกขัดจังหวะจะรอเพียง 5 นาทีก่อนลองใหม่ กระบวนการทั้งหมดมีเวลาสูงสุด 30 วินาที ข้อผิดพลาดระบุขั้นตอนและเวลาที่ใช้ หากคำสั่งหลักล้มเหลวจะยกเลิกการต่ออายุทันที การเพิกถอนอุปกรณ์ที่ยังค้างอยู่ไม่ถูกจำกัดด้วยช่วงเวลาหลังสำเร็จ และมีเวลาของตัวเอง 4 วินาที ใช้ `remote-cli seal renew` เพื่อต่ออายุโดยตรง หลังเข้าสู่ระบบสำเร็จจะบังคับรีเฟรชหนึ่งครั้ง
- **Root client:** ไคลเอ็นต์ผู้ดูแลระบบขั้นสูงที่เก็บเวิร์กสเปซรูทใช้การออกตามบทบาทในส่วนถัดไปเพื่อแทนที่การต่ออายุทั่วไปนี้เพื่อหลีกเลี่ยงการลงนามกลับการอนุญาตที่ถูกลบใหม่
- **Manual:** ยังคงใช้งานได้ `remote-cli seal renew` (ค่าเริ่มต้น `--before 30 --days 90`) กฎเดียวกันกับการต่ออายุอัตโนมัติ

```bash
# 按工作区用户名授权对方每一把已登记的钥匙，并发布到 Exchange（默认 90 天）
remote-cli seal grant --device lab-router --to alice --level view
# 只打印 token，不上传
remote-cli seal grant --device lab-router --operator-key <KEY> --no-publish
remote-cli seal keys          # 工作区公钥目录
remote-cli seal sync          # 拉取签给自己钥匙的 grant 并导入本地
remote-cli seal renew         # 手动续签，默认 --before 30 --days 90
```

## ผู้ดูแลระบบขั้นสูงของพื้นที่ทำงานคือรากฐานของความไว้วางใจ

รากของพื้นที่ทำงานคือคีย์ Ed25519 บนไคลเอ็นต์ผู้ดูแลระบบระดับพิเศษ อุปกรณ์จะเชื่อถือเฉพาะคีย์สาธารณะรูทนี้เท่านั้น และสามารถตรวจสอบการอนุญาต `rsg1.` ของรูทได้ตลอดสายใบรับรอง `rsc1.` ไคลเอนต์รูทจะออกและต่ออายุการอนุญาตสมาชิกโดยอัตโนมัติตามบทบาทของบัญชี Exchange ยังคงไม่น่าเชื่อถือ: จะบันทึกเฉพาะบันทึก root ของ `rsr1.` ที่ลงนามด้วยตนเอง, บันทึกการหมุนเวียน `rsx1.` และคำเชิญ `rsi1.` และตรวจสอบรูปแบบกับลายเซ็น Ed25519 โดยไม่ต้องตัดสินใจว่าใครเป็นผู้อนุมัติ Exchange ที่เป็นอันตรายสามารถระงับบันทึกหรือกรอกบัญชีรายชื่อด้วยสมาชิกปลอม มันไม่สามารถปลอมแปลงลายเซ็นรูตได้ การอนุมัติอัตโนมัติจะถูกเปิดใช้งานตามค่าเริ่มต้นใน CLI, Web และ Android และคีย์สาธารณะใหม่ในบัญชีรายชื่อจะถูกลงนามโดยตรงจากไคลเอ็นต์รูทเหล่านี้ เฉพาะเมื่อมีการปิดการอนุมัติอัตโนมัติอย่างชัดเจนเท่านั้น กุญแจสาธารณะใหม่ที่ไม่เคยได้รับการอนุมัติและไม่ได้รับการเผยแพร่โดยรูทนี้จะยังคงอยู่ในรายการที่รอดำเนินการ

### รูปแบบความน่าเชื่อถือและการแลกเปลี่ยน

- **กุญแจสาธารณะรูทได้รับการแก้ไขในการใช้งานครั้งแรก (TOFU) ** เมื่อไคลเอนต์เห็นบันทึกรูทที่ตรวจสอบแล้วเป็นครั้งแรก จะทำการแก้ไขกุญแจสาธารณะในเครื่อง CLI เขียน `seal-root.json` (0600) ถัดจาก `config.toml` และคีย์คือ `服务器|工作区 id` เว็บ Console ใช้ `localStorage` (`remote.seal.root.<工作区 id>`) Android Console ใช้ SecureStore (`remote.seal.rootstate` วางใน JSON เดียวกันโดยเซิร์ฟเวอร์และพื้นที่ทำงาน) จากนั้นตรวจสอบลายเซ็นตนเองและลายนิ้วมือทุกครั้ง หากคีย์สาธารณะรูทคงที่มีการเปลี่ยนแปลง จะมีการออกคำเตือนเสียงดัง ** ไคลเอนต์จะไม่เปลี่ยนรหัสผ่านโดยอัตโนมัติ** (อุปกรณ์จะเปลี่ยนรหัสผ่านโดยอัตโนมัติตามบันทึกการหมุน `rsx1.` ดูด้านบน) หลังจากตรวจสอบลายนิ้วมือแล้ว ให้ดำเนินการ `remote-cli seal root --trust <key>` อย่างชัดเจน หรือคลิก "เชื่อถือคีย์สาธารณะรูทใหม่" ในคอนโซล
- **Invitation เปิดให้ลงทะเบียนเท่านั้น ** Root public key ออก `rsi1.` (รหัสพื้นที่ทำงาน, root public key, nonce 16 ไบต์, เวลาหมดอายุ) ผู้ดูแลระบบ ผู้ปฏิบัติงาน และไคลเอนต์ที่มีบัญชีแบบอ่านอย่างเดียวจะดึงคำเชิญเมื่อเข้าสู่ระบบ ปฏิบัติตามคำสั่ง โหลดเพจ หรือเมื่อแอปพลิเคชันกลับสู่เบื้องหน้า ตรวจสอบลายเซ็นด้วยรหัสสาธารณะรูทคงที่ จากนั้นใช้ `POST /api/seal/keys` ดั้งเดิมเพื่อลงทะเบียนรหัสสาธารณะของผู้ปฏิบัติงาน คำเชิญไม่ใช่การอนุญาต
- **ออกตามบทบาท อนุมัติคีย์ใหม่ภายในเครื่อง ** ไคลเอ็นต์ผู้ดูแลระบบระดับสูงซึ่งมีคีย์สาธารณะรูทอยู่ อ่าน `GET /api/seal/roster` (ผู้ดูแลระบบระดับสูงเท่านั้น รวมถึงสมาชิกที่ปิดใช้งาน บทบาท `operator_device_grants` คีย์สาธารณะที่ลงทะเบียน) และสิทธิ์ที่เผยแพร่ของตนเอง และคำนวณการอนุญาตที่คาดหวัง กุญแจสาธารณะที่ลงทะเบียนเดียวกันจะใช้ช่วงชุดนี้ร่วมกัน:
  - ผู้ดูแลระบบขั้นสูงและผู้ดูแลระบบที่ไม่จำกัด: `manage` `*`
  - ผู้ดูแลระบบที่ถูกจำกัด: ได้รับอนุญาตจากอุปกรณ์ `can_manage` คือ `manage` มิฉะนั้น `view`
  - ตัวดำเนินการที่ไม่จำกัด: `view` `*` รวมถึง `manage` สำหรับอุปกรณ์ `can_manage` แต่ละตัว
  - ผู้ดำเนินการที่ถูกจำกัด: ต่อการตรวจสอบอุปกรณ์ `manage` หรือ `view`
  - อ่านอย่างเดียวไม่จำกัด: `view` `*`
  - จำกัดการอ่านอย่างเดียว: `view` ต่ออุปกรณ์ที่ได้รับอนุญาต บัญชีแบบอ่านอย่างเดียวไม่สามารถใช้งานอุปกรณ์ได้ ดังนั้นแม้แต่ `can_manage` ก็จะไม่ถูกส่ง `manage`
  - สมาชิกผู้พิการ: ห้ามโพสต์
  - ค่าเริ่มต้น 90 วัน ต่ออายุโดยเหลือเวลาน้อยกว่า 30 วัน
- **Aใบรับรองระดับกลางของผู้ดูแลระบบ ** Root ไคลเอ็นต์ออก `rsc1.` ให้กับผู้ดูแลระบบที่เปิดใช้งาน (`manage`, `can_issue`, `max_path_len` คือ 0, 90 วัน) ขอบเขตอุปกรณ์ของผู้ดูแลระบบที่ไม่จำกัดคือ `*` ผู้ดูแลระบบแบบจำกัดคืออุปกรณ์ที่ได้รับการกำหนดให้ `can_manage` ต่ออายุเหลือเพียง 30 วันเท่านั้น จะไม่มีการออกใบรับรองสำหรับกุญแจสาธารณะใหม่ที่ยังไม่ได้รับการอนุมัติ เมื่อไคลเอ็นต์ของผู้ดูแลระบบถือใบรับรองระดับกลางที่ยังคงใช้ได้อยู่ ไคลเอ็นต์จะใช้คีย์ของตัวเองและสายโซ่นี้เพื่อออกการอนุญาตให้กับสมาชิกที่จัดการ และขอบเขตคือจุดตัดของใบรับรองและการมอบหมายอุปกรณ์ หากรูทยังมีสิทธิ์ใหม่ซึ่งครอบคลุมโอเปอเรเตอร์ อุปกรณ์ และระดับเดียวกันอยู่แล้ว (`*` ของรูทครอบคลุมอุปกรณ์เฉพาะ `manage` ครอบคลุม `view`) ก็จะไม่เซ็นชื่ออีกต่อไป การเพิกถอนใบรับรองจาก Exchange จะหยุดการแจกจ่ายเพิ่มเติมเท่านั้น สำเนาที่มีอยู่แล้วในอุปกรณ์ยังคงใช้ได้จนกว่าจะหมดอายุ การเพิกถอนแบบถาวรอาศัย `revoked_keys` ในนโยบายด้านอุปกรณ์
- **การอนุมัติอัตโนมัติเปิดอยู่ตามค่าเริ่มต้น** ในเว็บ Console (`localStorage`) และ Android Console (SecureStore) เมื่อบันทึกไม่มีฟิลด์นี้ จะถือว่าเปิดอยู่ (ขาด = เปิด) เฉพาะ `false` ที่ชัดเจนเท่านั้นที่ยังคงปิดอยู่ รายการที่ปิดไปแล้วจะไม่ถูกเปิดอีกครั้งตามตรรกะเริ่มต้น หลังจากเปิดแล้ว ไคลเอ็นต์รูทจะลงชื่อโดยตรงตามบัญชีรายชื่อ **weaker**: Exchange ที่ถูกบุกรุกสามารถแทรกสมาชิกปลอมและรับลายเซ็นได้ทันที สำเนาสวิตช์คือ "อนุมัติคีย์สาธารณะใหม่โดยอัตโนมัติ" CLI เปิดใช้งานตามค่าเริ่มต้นเช่นเดียวกับ Web/Android Console หากไม่มีไฟล์ `seal-approved.json` หรือฟิลด์ `auto_approve` จะถือว่าเปิดอยู่ ส่วน `"auto_approve": false` ที่ระบุไว้อย่างชัดเจนจะยังคงปิด ใช้ `remote-cli seal approve --no-auto-approve` เพื่อปิด หรือ `remote-cli seal approve --auto-approve` เพื่อเปิดอีกครั้ง โดยระบบจะบันทึกตัวเลือกนี้ หลังจากปิด คีย์ที่ไม่เคยได้รับการอนุมัติหรือเผยแพร่โดยคีย์สาธารณะรูทนี้จะเข้าสู่รายการที่รอดำเนินการในเครื่อง (CLI `seal-approved.json`, Web `localStorage`, Android SecureStore) `remote-cli seal pending` แสดงชื่อผู้ใช้และลายนิ้วมือ `seal approve --all` หรือ `seal approve <key>…` จะออกทันทีเมื่อได้รับอนุมัติ สำหรับคีย์ที่ได้รับการอนุมัติ เช่นเดียวกับคีย์ที่ออกโดยคีย์สาธารณะรูทนี้ การต่ออายุและการเปลี่ยนแปลงบทบาทจะเสร็จสมบูรณ์โดยอัตโนมัติ การออกคีย์สาธารณะของลูกค้าไม่จำเป็นต้องได้รับการอนุมัติอีกครั้ง
- **ลงชื่อเข้าใช้ทันทีที่คุณเปิดขึ้นมา ** ไคลเอ็นต์รูทจะถูกเซ็นชื่อทันทีเมื่อเว็บ Console เข้าสู่พื้นที่ทำงาน และ Android เปิด "End-to-End Encryption" รอบนี้ไม่ต้องควบคุมปริมาณ 12 ชั่วโมง การรีเฟรชอัตโนมัติหลังจากการเข้าพักเดิมยังคงถูกควบคุมปริมาณ การเปลี่ยนสวิตช์หรือการคลิก "อนุมัติ" จะบังคับให้คุณลงชื่อเข้าใช้อีกครั้ง
- **กล่องโต้ตอบบัญชี ป้ายสถานะ และบรรทัดความล้มเหลว ** สวิตช์เว็บ Console และรายการที่รอดำเนินการอยู่ในกล่องโต้ตอบ "บัญชี" บนหน้าอุปกรณ์ ("บัญชี" บนแถบเครื่องมือ) เมื่อมีคีย์ที่รอการอนุมัติ ป้าย "คีย์สาธารณะ N ที่รอการอนุมัติ" จะแสดงบนแถบเครื่องมือเดียวกัน คลิกป้ายเพื่อเปิดกล่องโต้ตอบนี้ เมื่อการเซ็นชื่อล้มเหลว จะมีบรรทัดสถานะในแถบเครื่องมือ: "การเซ็นชื่อรูทของพื้นที่ทำงานล้มเหลว:..." โทเค็นการปิดผนึกและวัสดุกุญแจแบบยาวจะถูกลบออกจากบรรทัดนี้ สวิตช์ Android ป้ายสถานะประโยคเดียวกัน และคำแนะนำความล้มเหลวในบรรทัดเดียวกันอยู่ที่หน้าการเข้ารหัสจากต้นทางถึงปลายทาง
- **Downgrade จะไม่เพิกถอนการอนุญาตเก่าบนอุปกรณ์ ** เมื่อสมาชิกถูกลบ ปิดใช้งาน หรือคีย์สาธารณะหายไปจากไดเร็กทอรี ให้ลบสิทธิ์ออกจาก Exchange หยุดการต่ออายุ และลองเขียน `revoked_keys` บนอุปกรณ์ที่ออนไลน์และรองรับเซสชันที่ปิดผนึก เมื่อบทบาทถูกดาวน์เกรด จะมีการแลกเปลี่ยนเฉพาะสิทธิ์ใหม่เท่านั้น และคีย์จะไม่ถูกใส่ลงใน `revoked_keys` ทุนเก่าที่นำเข้าบนอุปกรณ์ยังคงใช้ได้ก่อนหมดอายุ คีย์สาธารณะรูทจะไม่ถูกใส่ไว้ในรายการเพิกถอน การเพิกถอนที่ไม่เสร็จสิ้นภายในงบประมาณ 4 วินาทีจะถูกบันทึกไว้ในเครื่องและจะดำเนินการต่อด้วยคำสั่งถัดไป โดยไม่ต้องอยู่ภายใต้ขีดจำกัดการควบคุมปริมาณการออก 12 ชั่วโมง เมื่อคีย์กลายเป็นการอนุญาตที่ต้องการอีกครั้ง การเพิกถอนจะถูกเพิกถอนบนอุปกรณ์ที่ถูกเพิกถอนสำเร็จเท่านั้น
- **Root บันทึกจะถูกสร้างขึ้นโดยอัตโนมัติ ** เมื่อบัญชีผู้ดูแลระบบขั้นสูงถูกสร้างขึ้น (`register`/บัญชีแรกในพื้นที่ทำงาน) หรือการเข้าสู่ระบบผู้ดูแลระบบขั้นสูงในภายหลัง หากไม่มีบันทึกรากบน Exchange ไคลเอนต์ของผู้ดูแลระบบขั้นสูง (CLI / Web / Android) จะเผยแพร่คีย์ของตัวเองเป็นรูทในเครื่อง และส่งคำเชิญ 90 วันทันที หลังจากนั้น เมื่อคำเชิญหายไปหรือจะหมดอายุภายใน 30 วัน คำเชิญจะถูกรีเฟรชพร้อมกับการควบคุมการออก `remote-cli seal root --publish` ยังคงสามารถดำเนินการได้ด้วยตนเอง

### การตั้งค่าครั้งเดียว

1. ผู้ดูแลระบบระดับสูงลงทะเบียนหรือล็อกอินเข้าสู่ไคลเอนต์ใดๆ: รูทจะถูกสร้างขึ้นและประกาศโดยอัตโนมัติ (CLI พิมพ์พรอมต์บน stderr; คอนโซลจะแสดงพรอมต์ขึ้นมา)
2. เก็บคีย์ส่วนตัวของรูทไว้ (เลือกอย่างใดอย่างหนึ่งหรือทั้งสองอย่าง):
   - เพียงเก็บไว้เองและไม่อัปโหลดอะไรเลย: `remote-cli seal root --export root.rsk` (ไฟล์ 0600; `-` ส่งออกไปยัง stdout) หรือ "ส่งออกคีย์ส่วนตัวรูท" ในคอนโซล
   - การตั้งค่าการสำรองข้อมูลบนคลาวด์ด้วยตนเอง: `remote-cli seal backup` หรือคอนโซล "Root Private Key Cloud Backup" รหัสผ่านสามารถเว้นว่างไว้ได้ แต่หากรหัสผ่านว่างเปล่าหรือคาดเดาง่ายเกินไป Exchange จะสามารถปลดล็อกคีย์ส่วนตัวรูทและควบคุมอุปกรณ์ทั้งหมดได้
3. ตราบใดที่อุปกรณ์รองรับเซสชันที่ปิดผนึกและเชื่อมต่อกับ Exchange อุปกรณ์จะแก้ไขรูท (TOFU) โดยอัตโนมัติ แนวทางที่เชื่อถือได้มากขึ้นคือตั้งค่า `remote-agent seal root <根公钥>` บนอุปกรณ์ก่อนออนไลน์
4. สมาชิกใหม่จะลงทะเบียนคีย์สาธารณะของตนหลังจากเข้าสู่ระบบ การอนุมัติอัตโนมัติจะถูกเปิดใช้งานตามค่าเริ่มต้นใน CLI, Web และ Android และไคลเอนต์รูทจะลงนามโดยตรง หากการอนุมัติอัตโนมัติปิดอยู่: มุมมอง `remote-cli seal pending`, `remote-cli seal approve --all` (หรือคอนโซล "อนุมัติ") จะออกทันทีหลังจากการอนุมัติ

```bash
remote-cli seal root                       # 显示已固定的根公钥和指纹
remote-cli seal root --export root.rsk     # 导出根私钥（rsk1. 文本）
remote-cli seal root --import root.rsk     # 在另一台机器装上根私钥（或 seal restore）
remote-cli seal root --rotate              # 生成新根，旧根签 rsx1. 轮换，自动按新根重签成员授权
remote-cli seal invite                     # 单独刷新邀请（默认 90 天，--days N）
remote-cli seal pending
remote-cli seal approve --all              # 或 seal approve <key>…；--no-auto-approve 关闭自动批准（保持开启较弱）
```

### เปลี่ยนเป็นเครื่องอื่น/เปลี่ยนรูท

- **ใช้รูทบนอุปกรณ์อื่น:** หลังจากที่ผู้ดูแลระบบระดับสูงเข้าสู่ระบบแล้ว หากเครื่องไม่มีรูทที่เผยแพร่และมีการสำรองข้อมูลบนคลาวด์ `rsb1.` บน Exchange, CLI / Web / Android จะทำให้ ** พยายามกู้คืนโดยอัตโนมัติ** (ดู "การกู้คืนอัตโนมัติเมื่อเข้าสู่ระบบ") มิฉะนั้น ไคลเอนต์จะแจ้งให้นำเข้าด้วยตนเอง: `seal root --import <文件|->` วาง `rsk1.` ในคอนโซล จากนั้น "นำเข้าคีย์ส่วนตัวรูท" หรือกู้คืนข้อมูลสำรองที่เข้ารหัสจาก Exchange (`seal restore` / คอนโซล "กู้คืนจาก Exchange") สิ่งที่นำเข้าคือรูทปัจจุบัน และติดตั้งบนเครื่องท้องถิ่นเท่านั้นโดยไม่มีการเปลี่ยนแปลงบันทึกใดๆ
- **Rotation:** `seal root --rotate` (Console "สร้างรูทใหม่และหมุน") หรือนำเข้าคีย์ `rsk1.` ที่เป็น ** แตกต่างจากรูทปัจจุบัน** เมื่อเครื่องโลคัลเก็บรูทปัจจุบันไว้ ไคลเอ็นต์จะใช้รูทเก่าเพื่อลงนามบันทึกการหมุน `rsx1.` ร่วมกับ `rsr1.` `PUT /api/seal/root` ใหม่ Exchange จะพุชไปที่อุปกรณ์ออนไลน์ และอุปกรณ์จะตรวจสอบว่าการหมุนได้รับการลงนามโดยรูทคงที่ จากนั้นจึงเปลี่ยนเป็นรูทใหม่ และอุปกรณ์ออฟไลน์จะได้รับในครั้งถัดไปที่เชื่อมต่อ สมาชิกที่ได้รับอนุมัติโดยรูทเก่าจะถือว่าได้รับการอนุมัติบนเครื่องนี้ต่อไป จากนั้นการอนุญาตของสมาชิกทั้งหมดจะถูกลงนามอีกครั้งด้วยรูทใหม่ (90 วัน) โลคัลคีย์เก่ายังคงอยู่ `seal-identity.<毫秒>.previous.pk8` โปรดส่งออกใหม่หรือสำรองข้อมูลรูทใหม่หลังจากการหมุนเวียน
- ** รูทเก่าหายไป:** ไม่สามารถลงนามบันทึกการหมุนเวียนได้ ลูกค้าจะยังคงโฆษณารูทใหม่ แต่อุปกรณ์ที่มีการปักหมุดรูทเก่าจะไม่ยอมรับมัน ต้องดำเนินการ `remote-agent seal root <新根公钥>` ในแต่ละอุปกรณ์ และ agent จะรีสตาร์ท
- **การส่งมอบผู้ดูแลระบบระดับสูง:** โปรดดู "การส่งมอบผู้ดูแลระบบขั้นสูง = การส่งมอบใบรับรองหลัก" เพียงเปลี่ยนบทบาทของบัญชีไม่เพียงพอ อุปกรณ์จะเชื่อถือคีย์ส่วนตัวของรูท

## กู้คืนคีย์ส่วนตัวรูทโดยอัตโนมัติเมื่อเข้าสู่ระบบ

หลังจากที่ผู้ดูแลระบบขั้นสูงเข้าสู่ระบบสำเร็จแล้ว ** ก่อนที่จะสร้างข้อมูลประจำตัวที่ปิดผนึกในเครื่อง**, CLI / Web Console / Android Console จะใช้ตารางการตัดสินใจเดียวกัน (Rust `decide_auto_restore`, JS `decideAutoRestore`) เพื่อพยายามกู้คืนรูทของเวิร์กสเปซที่เผยแพร่จากการสำรองข้อมูล Exchange `rsb1.`:

1.**Skip:** ไม่ใช่ผู้ดูแลระบบระดับสูง เครื่องมีคีย์เดียวกันกับรูทที่เผยแพร่อยู่แล้ว การสำรองข้อมูลบนคลาวด์ถูกปิด (CLI: `REMOTE_SEAL_CLOUD_BACKUP=0`; เว็บ: `localStorage.seal.cloudBackup=off`; Android: SecureStore `remote.seal.cloudBackup=off`); ไม่มีการเผยแพร่รูทบน Exchange
2.**ดึงข้อมูลสำรอง:** `GET /api/seal/backup` (จำกัดด้วยจำนวนครั้งต่อชั่วโมง) เมื่อไม่มีการสำรองข้อมูล ระบบจะแจ้งว่ารูทอยู่ที่อื่นและไม่บล็อกการเข้าสู่ระบบ
3.** พยายามเงียบด้วยรหัสผ่านว่างเปล่า:** ถอดรหัสด้วยสตริงว่างก่อน เมื่อดำเนินการสำเร็จและคีย์สาธารณะที่ถอดรหัสแล้วเท่ากับรูทที่เผยแพร่ และเครื่องไม่มีคีย์อื่น คีย์ดังกล่าวจะถูกโหลดลงในเครื่องและแก้ไขเป็นรูทของพื้นที่ทำงาน
4. **Oมิฉะนั้นรหัสผ่านจะได้รับแจ้ง:** CLI พยายามอีกครั้ง `REMOTE_SEAL_BACKUP_PASSPHRASE` อินพุตจะถูกซ่อนอยู่ใน TTY; Web/Android จะส่งเฉพาะคำแนะนำ **non-blocking** เท่านั้น โปรดป้อนรหัสผ่านบนหน้าซีลแล้วคลิก "กู้คืนจาก Exchange" กล่องโต้ตอบการบล็อกไม่ปรากฏขึ้นและการเข้าสู่ระบบจะเสร็จสมบูรณ์ตามปกติ
5.**Security Boundary:** ห้ามเขียนทับคีย์ที่ปิดผนึกอื่นที่มีอยู่แล้วในเครื่องนี้โดยไม่แจ้งให้ทราบ (การกู้คืนต้องใช้ `seal restore --force` หรือคอนโซลเพื่อยืนยัน) กุญแจสาธารณะที่ถอดรหัสจะต้องเท่ากับรูทที่เผยแพร่ จะมีการเตือนเฉพาะความล้มเหลวและการเข้าสู่ระบบจะไม่เกิดขึ้น รหัสผ่าน เมล็ดพืช หรือข้อความเข้ารหัส `rsb1.` จะไม่ถูกพิมพ์
6.**คำเตือนความเสี่ยง (ไม่ปิดกั้น):** หลังจากติดตั้งรูทแล้ว ระบบจะแจ้ง "เครื่องหลายเครื่องที่ยึดรูทไว้จะเพิ่มความเสี่ยงของการรั่วไหล"; หากปลดล็อครหัสผ่านที่ว่างเปล่าสำเร็จ ระบบจะแจ้งว่า Exchange สามารถปลดล็อคคีย์ส่วนตัวรูทได้

## การส่งมอบผู้ดูแลระบบระดับสูง = การส่งมอบรูทคีย์


บทบาทของบัญชีผู้ดูแลระบบขั้นสูงและรูทพื้นที่ทำงานที่อุปกรณ์ไว้วางใจจะแยกจากกัน การโอนบทบาทเพียงอย่างเดียวไม่ได้ทำให้ผู้ดูแลระบบระดับสูงคนใหม่ได้รับคีย์ส่วนตัวรูทหรือความน่าเชื่อถือของอุปกรณ์ การโอนย้ายจะถ่ายโอนคีย์ส่วนตัว **root** ตามด้วยการหมุนเวียนทันทีเพื่อทำให้คีย์ที่ถ่ายโอนเป็นโมฆะ

1. ผู้ดูแลระบบขั้นสูงปัจจุบันเริ่มต้นการถ่ายโอนในขณะที่ไคลเอ็นต์ภายในเครื่องเก็บ root ที่เผยแพร่: `POST /api/superadmin/transfer` โดยมีเนื้อความ `{operator_id, root_handover?}` `root_handover` เป็นแพ็คเกจ `rsb1.` ที่เข้ารหัสด้วยข้อความรหัสผ่าน โดยใช้อัลกอริธึมการสำรองข้อมูลบนคลาวด์ (Argon2id, 64 MiB, t=3, p=1 และ ChaCha20-Poly1305) ข้อความรหัสผ่านอาจว่างเปล่า CLI: `remote-cli seal handover <账号>`. เมื่อถ่ายโอนบทบาทผู้ดูแลระบบขั้นสูงใน Web/Android Console ไคลเอ็นต์ที่ถือคีย์ส่วนตัว root จะพร้อมท์ให้ใส่ข้อความรหัสผ่านเผื่อเลือกและรวมแพ็คเกจนี้ด้วย
2. Exchange ** บันทึกเท่านั้น ไม่ใช่ถอดรหัส** จะตรวจสอบว่าคำนำหน้าคือ `rsb1.` หรือ `rsk1.` ยาวไม่เกิน 16384 และประกอบด้วยอักขระที่ปลอดภัยต่อ URL แบบ ASCII เท่านั้น เมื่อไม่มี `root_handover` ในคำขอ ให้ลบแพ็คเกจการส่งมอบที่เหลือของเวิร์กสเปซ
3. ผู้ดูแลระบบขั้นสูงคนใหม่ `GET /api/superadmin/handover` ดึงข้อมูล `{blob, from_operator, created_at}` (หากไม่ใช่ 404) ปลดล็อกด้วยรหัสผ่าน ติดตั้งลงในเครื่องภายในเครื่อง จากนั้นดำเนินการหมุนเวียนแบบเดียวกับ "สร้างรูทใหม่และหมุน": รูทเก่าออกปัญหา `rsx1.` และเผยแพร่รูทใหม่ ในที่สุด `DELETE /api/superadmin/handover` CLI: `remote-cli seal accept-handover`. หน้าประทับตราของคอนโซลจะแสดง "รับการส่งมอบคีย์ส่วนตัวของรูท" เมื่อมีแพ็คเกจการส่งมอบ
4. **Rotation ทำให้สำเนาเก่าไม่ถูกต้อง ** สิ่งที่ส่งมอบคือรูทก่อนการหมุน อุปกรณ์ออนไลน์จะสลับไปใช้รูทใหม่ก็ต่อเมื่อมีการลงนามบันทึกการหมุนโดยรูทเก่าที่พวกเขาเชื่อถืออยู่แล้ว คีย์ส่วนตัวเก่าที่อยู่ในมือของรุ่นก่อนไม่สามารถลงนามในการอนุญาตรูทใหม่ได้ และไม่สามารถเปลี่ยนรูทของอุปกรณ์ที่ถูกแปลงได้ ก่อนที่การหมุนจะเสร็จสิ้น รหัสผ่านว่างหรือรหัสผ่านที่ไม่รัดกุม `rsb1.` ยังสามารถปลดล็อคได้ กรุณาส่งรหัสผ่านแยกต่างหาก

**นอกแบนด์:** เมื่อคุณไม่ต้องการใส่ไซเฟอร์เท็กซ์ลงใน Exchange ให้ใช้ `remote-cli seal handover <账号> --out-of-band` สำหรับ CLI เอาต์พุตมาตรฐานมี `rsb1.` เพียงบรรทัดเดียว และคำขอถ่ายโอนไม่มี `root_handover` ให้บรรทัดและรหัสผ่านนี้แก่อีกฝ่ายผ่านช่องทางอื่น และอีกฝ่ายจะดำเนินการ `remote-cli seal accept-handover <那一行>` คลิกยกเลิกบนพร้อมท์รหัสผ่านในคอนโซล (Android จะโอนบัญชีเท่านั้น) นอกจากนี้ยังถ่ายโอนบทบาทเท่านั้นและไม่ได้อัปโหลดคีย์ส่วนตัวของรูท ยังคงต้องส่งมอบคีย์ส่วนตัวรูทแยกต่างหาก (ส่งออก `rsk1.` หรือ `rsb1.` นอกแบนด์)

## การจับมือและรูปแบบเส้น

```text
CLI                         Exchange                       agent
 │ open_sealed{session_id,    │                               │
 │   device_id, hello}        │──sealed_open{session_id,hello}▶│ 验证 hello 签名、授权 → 级别
 │                            │◀─sealed_accept{session_id,     │
 │◀─sealed_accept (原样转发) ──│        accept}                │
 │ 验证设备签名 + pin          │                               │
 │ sealed_input{packet} ─────▶│──sealed_input────────────────▶│ 解密、校验级别、执行
 │ 二进制帧 kind=4 (PTY 键入)─▶│── 只改写帧头编号 ─────────────▶│
 │◀── sealed_event{packet,last}│◀── sealed_event ──────────────│ 所有输出在出站队列加密
 │◀── 二进制帧 kind=4 ─────────│◀── 二进制帧 kind=4 ───────────│
```

- `hello` = `{v, operator_key, operator_ephemeral, nonce, grant?, sig}` ลายเซ็นครอบคลุม `["remote-seal-v1/hello", v, device_id, operator_key, operator_ephemeral, nonce, grant.sig|""]` (อาร์เรย์ JSON เช่น ไบต์แบบบัญญัติ)
- ลายเซ็นรากบันทึก `rsr1.` ครอบคลุม `["remote-seal-v1/workspace-root", v, workspace_id, root_key, issued_at_ms]`; การหมุน `rsx1.` = `{v, workspace_id, old_root, new_root, issued_at_ms, sig}` ลงนามโดย `old_root` บน `["remote-seal-v1/root-rotation", v, workspace_id, old_root, new_root, issued_at_ms]` Exchange → agent: `{"type":"seal_root","token":"rsr1.…","rotation":"rsx1.…"?}` (ยอมรับเฉพาะข้อความควบคุมข้อความที่ชัดเจนจาก Exchange ซึ่งไม่ถูกต้องในช่องสัญญาณ)
- `accept` = `{v, device_key, device_ephemeral, nonce, level, sig}` ลายเซ็นครอบคลุม `["remote-seal-v1/accept", v, device_id, operator_key, operator_ephemeral, hello.nonce, hello.sig, device_key, device_ephemeral, nonce, level]` Exchange ที่เปลี่ยนเป็น `level` จะทำให้ลายเซ็นใช้ไม่ได้
- คีย์เซสชัน: `salt = SHA256(accept 签名字节)`, `PRK = HKDF-Extract(salt, X25519(o, b))`, `K_od = Expand("remote-seal-v1/operator_to_device")`, `K_do = Expand("remote-seal-v1/device_to_operator")`
- AEAD: ChaCha20-Poly1305, AAD = `remote-seal-v1`, nonce = ไบต์ของช่องสัญญาณ (0 ข้อความ / 1 ไบนารี่) ‖ สามศูนย์ไบต์ ‖ ตัวนับ `u64` (ปลายใหญ่) ตัวนับในแต่ละทิศทางและแต่ละช่องจะเพิ่มขึ้นอย่างเข้มงวดหนึ่งจาก 0; ความคลาดเคลื่อนใดๆ จะปิดเซสชันโดยตรง
- ข้อความ: `{"counter":n,"ciphertext":"<base64>"}`. ข้อความที่ชัดเจนคือ JSON: โอเปอเรเตอร์ → อุปกรณ์คือ `SealedRequest` (ข้อความ `DeviceDown` ที่มีอยู่ หรือข้อความการจัดการรูท `seal_policy_get` / `seal_policy_set {mode?, root_key?, revoke?, unrevoke?}`) อุปกรณ์ → โอเปอเรเตอร์คือ `SealedResponse` (ข้อความ `DeviceUp` ที่มีอยู่หรือ `seal_policy`) เลเยอร์ด้านใน `session_id` ถูกเขียนใหม่เป็นหมายเลขช่องสัญญาณโดย agent
- เฟรมไบนารี่: `StreamKind::Sealed = 4`, เพย์โหลด = `counter(u64 BE) ‖ AEAD(inner_kind ‖ bytes)`, `inner_kind` คือ 1/2/3 (stdout/stderr/PTY)
- แต่ละเซสชันที่ปิดผนึกมีการดำเนินการ **one** (หนึ่ง exec, หนึ่ง PTY, หนึ่งการถ่ายโอนไฟล์...) ตามโมเดลเซสชัน 1:1 ข้อความการยุติถูกเข้ารหัสโดย agent และทำเครื่องหมาย `last:true` ในเลเยอร์ด้านนอก Exchange เผยแพร่การตรวจสอบการกำหนดเส้นทางและทดแทนข้อมูลตามลำดับ โดยไม่จำเป็นต้องเข้าใจเนื้อหา
- `close_session` → `sealed_close{detach:false}` (สิ้นสุด); การตัดการเชื่อมต่อการทำงานและการบำรุงรักษา → `sealed_close{detach:true}` (ตัดสินโดย agent: ให้กด exec ค้างไว้เท่านั้น ถอดออก, ยุติอื่นๆ)
- ขนาดสูงสุด: ประมาณ 2.8 MiB สำหรับ ciphertext base64 (รองรับภาพหน้าจอ 2 MiB Android) บรรจุภัณฑ์ด้านนอก `MAX_SEALED_WIRE`

บรรทัดการป้องกันฝั่ง Agent: ข้อความธรรมดาที่เป็นของหมายเลขเซสชันที่ปิดผนึกในคิวขาออกจะถูกเข้ารหัสหรือละทิ้งเท่านั้น และหมายเลขจะถูกแยกออกอย่างถาวรหลังจากเซสชันสิ้นสุด (เอาต์พุตล่าช้าของกระบวนการระงับจะถูกยกเลิก แทนที่จะถูกส่งในรูปแบบข้อความธรรมดา) งานการระงับที่เริ่มต้น/แนบผ่านเซสชันที่ปิดผนึกจะไม่ปรากฏในรายการของเส้นทางเก่า และไม่สามารถแนบ/หาง/แมว/ฆ่าโดยเส้นทางเก่าได้

## ส่งออกและสำรองข้อมูลคีย์ส่วนตัวรูท

คีย์ส่วนตัวรูทสามารถควบคุมอุปกรณ์ทั้งหมดในพื้นที่ทำงาน วิธีการจัดเก็บข้อมูลถูกกำหนดโดยผู้ดูแลระบบขั้นสูง **Console จะอัปโหลดการสำรองข้อมูลข้อความรหัสผ่านว่างตามค่าเริ่มต้น ลบออกเพื่อปิดการใช้งานการบันทึกอัตโนมัติ**:

- **Export (ไม่มีการอัปโหลด): ** `rsk1.` + base64url (ไม่มีช่องว่างภายใน) เมล็ด 32 ไบต์ CLI `seal root --export FILE|-` (ไฟล์ 0600), คอนโซล "ส่งออกคีย์ส่วนตัวของรูท" (เว็บจะแสดงในกล่องอินพุต Android จะถูกคัดลอกไปยังคลิปบอร์ด) ใครก็ตามที่ได้รับมันสามารถควบคุมอุปกรณ์ทั้งหมดได้
- **Eเข้ารหัสการสำรองข้อมูลไปยัง Exchange (อัตโนมัติใน Console, คู่มือใน CLI): ** เฉพาะผู้ดูแลระบบระดับสูงเท่านั้นที่สามารถอัปโหลดได้หากโลคัลคีย์เป็นรูทที่เผยแพร่ ไซเฟอร์เท็กซ์เป็นโทเค็น `rsb1.` เวอร์ชัน (JSON ที่เข้ารหัส base64url สูงสุด 4,096 อักขระ) คีย์ได้มาจากรหัสผ่านโดย Argon2id และพารามิเตอร์การผลิตได้รับการแก้ไขที่หน่วยความจำ 64 MiB การวนซ้ำ 3 และความขนาน 1 ซึ่งเขียนในโทเค็น Salt คือ 16 ไบต์ nonce ของ ChaCha20-Poly1305 คือ 12 ไบต์ และสร้างขึ้นแบบสุ่มสำหรับการสำรองข้อมูลแต่ละครั้ง ข้อมูลการรับรองความถูกต้องเพิ่มเติมคือ `remote-seal-v1/key-backup:{version}:{public_key}` หากเวอร์ชัน คีย์สาธารณะ หรือข้อความเข้ารหัสมีการเปลี่ยนแปลง การตรวจสอบจะล้มเหลว หลังจากปลดล็อคแล้ว จะต้องตรวจสอบว่ากุญแจสาธารณะที่ได้มาจาก Seed นั้นเท่ากับกุญแจสาธารณะในโทเค็น ตัวถอดรหัสจะปฏิเสธพารามิเตอร์ที่อ่อนแอเกินไปหรือใหญ่เกินไปก่อนที่จะรัน Argon2 (หน่วยความจำ 8 KiB–256 MiB, การถอดรหัสการผลิตอย่างน้อย 64 MiB; การวนซ้ำ 1–8, การผลิตอย่างน้อย 3; ความขนานสามารถมีได้เพียง 1 เท่านั้น)
- **รหัสผ่านเป็นทางเลือก ไม่บังคับความเข้มงวด ** รหัสผ่านที่ว่างเปล่ายังใช้ Argon2id (รหัสผ่านเป็นสตริงว่าง) และรูปแบบก็เหมือนกัน ลูกค้าแจ้งเท่านั้น: เมื่อรหัสผ่านว่างเปล่าหรืออ่อนแอ (น้อยกว่า 12 ตัวอักษร, หมวดหมู่อักขระน้อยเกินไป, ซ้ำ, รหัสผ่านทั่วไป), CLI / เว็บ / Android จะเตือนอย่างชัดเจน: **Exchange (หรือบุคคลที่สามารถอ่านฐานข้อมูลได้) สามารถปลดล็อคคีย์ส่วนตัวรูทและควบคุมอุปกรณ์ทั้งหมด ** Rust และ JavaScript ใช้ ชุดกฎพร้อมท์เดียวกันและเวกเตอร์ทดสอบชุดเดียวกัน (รวมถึงเวกเตอร์รหัสผ่านว่างเปล่า)

`PUT` / `GET` / `DELETE /api/seal/backup` ให้บริการเฉพาะบัญชีเข้าสู่ระบบปัจจุบันเท่านั้น `PUT` และ `GET` อนุญาตเฉพาะผู้ดูแลระบบระดับสูงเท่านั้น เนื้อหาคำขอคือ `{blob}` Exchange ตรวจสอบคำนำหน้าและความยาว `rsb1.` และไม่เคยถอดรหัส `GET` แต่ละบัญชี แต่ละกระบวนการ สูงสุด 5 ครั้งต่อชั่วโมง หากเกิน จะส่งกลับ 429; สิ่งนี้จะทำให้การคาดเดาออนไลน์ช้าลงเท่านั้น และรหัสผ่านที่ไม่รัดกุมสามารถหมดลงแบบออฟไลน์ได้หลังจากที่คัดลอกไลบรารีแล้ว การอ่าน การเขียน และการลบจะถูกตรวจสอบ และรายละเอียดเป็นเพียงจำนวนไบต์เท่านั้น

- CLI: `remote-cli seal backup` (ซ่อนการป้อนรหัสผ่านสองครั้ง คุณสามารถป้อนและปล่อยว่างไว้ได้โดยตรง หรือ `--passphrase-stdin`) `remote-cli seal restore` (เพิ่ม `--force` เมื่อมีคีย์อื่น และเก็บคีย์เก่าไว้เป็น `seal-identity.<毫秒>.previous.pk8`) หลังจากการกู้คืน ให้แก้ไขเป็นรูทเวิร์กสเปซของเครื่อง นอกจากนี้ จะมีการพยายามกู้คืนโดยอัตโนมัติเมื่อเข้าสู่ระบบ (ดูหัวข้อก่อนหน้า) อย่าพิมพ์คีย์ส่วนตัว รหัสผ่าน หรือข้อความเข้ารหัส
- เว็บ Console: คีย์ส่วนตัวรูทจะต้องสามารถส่งออกได้ เมื่อผู้ดูแลระบบระดับสูงสร้างรูท เบราว์เซอร์ที่เก็บคีย์ WebCrypto ที่ไม่สามารถส่งออกได้จะใช้เมล็ดพันธุ์ในเครื่องใน IndexedDB เป็นรูทแทน บูตสแตรปเข้าสู่ระบบจะดำเนินการต่อโดยอัตโนมัติก่อนที่จะสร้างข้อมูลประจำตัว `localStorage.seal.cloudBackup=off` สามารถปิดได้
- Android Console: เมล็ดอยู่ในร้านค้าที่ปลอดภัยสำหรับงานแสดงสินค้า `bootstrapSeal` จะกู้คืนโดยอัตโนมัติก่อนเช่นกัน `remote.seal.cloudBackup=off` สามารถปิดได้

## ใช้

```bash
remote-cli seal show                       # 生成/显示本机操作员公钥与指纹
remote-cli exec lab-router -- uname -a
remote-cli shell lab-router
remote-cli seal policy lab-router          # 根：查看设备授权与吊销策略

# 根给同事手动授权（通常不需要：根客户端按角色自动签发）（默认 90 天；对方已登录的客户端会自动登记公钥）
remote-cli seal grant --device lab-router --to alice --level view
# 或仍用公钥；--no-publish 只打印 token
remote-cli seal grant --device lab-router --operator-key <KEY> --level view --no-publish
# 手动续签即将到期的已发布授权（默认 --before 30 --days 90；上线时也会自动续签）
remote-cli seal renew
# 同事：拉取 Exchange 上签给自己的授权（登录后也会自动 sync）
remote-cli seal sync
# 仍可手动导入
remote-cli seal import-grant rsg1.xxxxx

# 根私钥：导出（不上传）或可选的加密云端备份
remote-cli seal root --export root.rsk
remote-cli seal backup
remote-cli seal restore                    # 已有不同钥匙时加 --force

# 带外固定设备指纹（在设备上 `remote-agent seal show`）
remote-cli seal trust-device lab-router <DEVICE_KEY>
```

**Web Console / Android Console:** การทำงานของอุปกรณ์ทั้งหมดใช้เซสชันที่ปิดผนึกดั้งเดิม: คำสั่ง, Hold List/kill/tail/cat, เทอร์มินัล, ไฟล์, ภาพหน้าจอและอินพุต (Android/เดสก์ท็อป), โครงสร้างอินเทอร์เฟซ, เซ็นเซอร์, ubus, บันทึกระบบ และ การอัปเดตตัวเองทั้งหมดต้องผ่านอุโมงค์ที่ปิดสนิท "🔒 การเข้ารหัสจากต้นทางถึงปลายทาง" ปรากฏบนหน้า รายละเอียดอุปกรณ์เว็บหรือการตั้งค่า Android → แผง "การเข้ารหัสจากต้นทางถึงปลายทาง" เทียบเท่ากับ CLI: แสดงคีย์สาธารณะ/ลายนิ้วมือในเครื่อง, รูทพื้นที่ทำงาน (ผู้ดูแลระบบขั้นสูง: ส่งออก/นำเข้า/หมุนคีย์ส่วนตัวรูท, รับการส่งมอบคีย์ส่วนตัวรูท, อนุมัติคีย์สาธารณะใหม่), ดู/แก้ไขคีย์อุปกรณ์และนโยบายการเพิกถอน (ต้องใช้รูท), การอนุญาตปัญหา (โทเค็นเอาท์พุต `rsg1.`, CLI สามารถทำได้โดยตรง `seal import-grant`) การอนุญาตการนำเข้า รหัสสาธารณะของอุปกรณ์ที่เชื่อถือได้ และการสำรองข้อมูลบนคลาวด์อัตโนมัติของคีย์ส่วนตัวรูท (รหัสผ่านสามารถเว้นว่างได้ และ Exchange จะได้รับคำเตือนอย่างชัดเจนหากรหัสผ่านไม่รัดกุมและสามารถปลดล็อคได้) เมื่อเชื่อมต่อกับลายนิ้วมือของอุปกรณ์แบบคงที่ (TOFU) เป็นครั้งแรก การเปลี่ยนแปลงลายนิ้วมือในภายหลังจะถูกปฏิเสธโดยตรงและขอให้ตรวจสอบ สมาชิกสามัญจะมีปุ่มที่แตกต่างกันสามปุ่มบน CLI เบราว์เซอร์ และโทรศัพท์มือถือ หลังจากการลงทะเบียนแต่ละครั้ง ไคลเอนต์รูทจะออกให้ตามบทบาทของพวกเขา หากผู้ดูแลระบบขั้นสูงต้องการรูทบนอุปกรณ์หลายเครื่อง เขาหรือเธอสามารถอัปโหลดการสำรองรหัสผ่านและกู้คืนข้อมูลโดยอัตโนมัติเมื่อเข้าสู่ระบบในอุปกรณ์ใหม่ หรือนำเข้าคีย์ส่วนตัวรูทลงในแต่ละอุปกรณ์

บนอุปกรณ์: `remote-agent seal show | root <key> | forget-root | revoke <key>` (รีสตาร์ท agent หลังจากการเปลี่ยนแปลงในเครื่อง การเปลี่ยนแปลงผ่านเซสชัน `seal policy` ที่ปิดผนึกจะมีผลทันที)

## พฤติกรรมเริ่มต้น

| ขอบเขต | พฤติกรรมปัจจุบัน |
|---|---|
| CLI, เว็บ Console, Android Console | การทำงานของอุปกรณ์ทั้งหมดใช้เซสชันที่ปิดสนิท โดยไม่มีสวิตช์ปิดใช้หรือการใช้ข้อความธรรมดาสำรอง |
| นโยบายท้องถิ่นของอุปกรณ์ | ทุกค่านโยบาย รวมถึง `allow` จะปฏิเสธการดำเนินการกับข้อความธรรมดา |

- อุปกรณ์ใหม่ปักหมุดรูทพื้นที่ทำงานในการเชื่อมต่อกับ Exchange (TOFU) รูทและสิทธิ์ที่ลงนามแล้วอนุญาตการดำเนินการ หาก Exchange อาจไม่น่าเชื่อถือในการเชื่อมต่อครั้งแรก ให้เรียกใช้ `remote-agent seal root <key>` ในเครื่องก่อนที่จะนำอุปกรณ์ออนไลน์
- คีย์ข้อมูลประจำตัวที่ไม่พร้อมใช้งานหรือนโยบายที่เสียหายล้มเหลวในการปิด ซ่อมแซมคีย์และนโยบายในเครื่องก่อนเชื่อมต่อใหม่
