feat!: genesis-pinned operator set, server identity with an encrypted control link, key-less join codes; release key continuity
Join codes need no passkey touch and carry only public values (genesis fingerprint, owner credentials, installer hashes). Nodes pin the genesis and owners at enrollment and only follow passkey-signed operator set versions. The panel's server key is listed in the set; pinned nodes require its signature and run the control link under AES-256-GCM. Password sessions carry no capabilities. The updater adopts a next_key from a signed manifest. Bump version to 0.2.0. [CI SKIP]
This commit is contained in:
1 parent
9bb6841dce
commit
a2f88a344b
89 files changed
+2145
-1195
No files matched your search
@@ -0,0 +1,60 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { api } from '@shared/api/client'
|
||||
import type { Page, PasskeyDto, TrustStateDto } from '@shared/api/types'
|
||||
|
||||
/**
|
||||
* 选这个节点的属主:允许授权它 extension 会话(web ssh 等,以后)的 passkey。只是选,不需要触碰 Passkey:
|
||||
* 选定的凭证 id 会写进安装命令,节点装上时固定在本地,之后面板改不了。默认是操作员集合里的全部。
|
||||
* 还没有建立信任根时不显示选项,只提示去建立。
|
||||
*/
|
||||
export function OwnerPicker({ value, onChange }: { value: string[] | null; onChange: (next: string[]) => void }) {
|
||||
const [trust, setTrust] = useState<TrustStateDto>()
|
||||
const [own, setOwn] = useState<PasskeyDto[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
let alive = true
|
||||
api
|
||||
.get<TrustStateDto>('/api/admin/trust')
|
||||
.then((t) => {
|
||||
if (!alive) return
|
||||
setTrust(t)
|
||||
if (value === null) onChange(t.operators ?? [])
|
||||
})
|
||||
.catch(() => undefined)
|
||||
api
|
||||
.get<Page<PasskeyDto>>('/api/admin/auth/passkeys')
|
||||
.then((list) => alive && setOwn(list.items))
|
||||
.catch(() => undefined)
|
||||
return () => {
|
||||
alive = false
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
if (!trust) return null
|
||||
if (!trust.initialized) {
|
||||
return <div className="notice warn">还没有建立信任根:这个节点装上后不会固定属主,也不会校验面板的操作员集合。先到「系统 → 信任」用 Passkey 建立。</div>
|
||||
}
|
||||
|
||||
const chosen = new Set(value ?? [])
|
||||
const name = (id: string) => own.find((p) => p.id === id)?.name ?? `其他账号的 Passkey(${id.slice(0, 8)}…)`
|
||||
return (
|
||||
<div className="stack" style={{ gap: 4 }}>
|
||||
{(trust.operators ?? []).map((id) => (
|
||||
<label key={id} className="switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={chosen.has(id)}
|
||||
onChange={(e) => {
|
||||
const next = new Set(chosen)
|
||||
if (e.target.checked) next.add(id)
|
||||
else next.delete(id)
|
||||
onChange([...next])
|
||||
}}
|
||||
/>
|
||||
{name(id)}
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user