返回 CodeWhale
tier.ts
根目录 / crates / tui / extension-host / src / tier.ts
1 /**
2 * Which trust tier this host process serves.
3 *
4 * The core starts one host process per tier from the same bundle and says
5 * which with `--tier=plugin|builtin`. A plugin-tier host runs reviewed
6 * third-party plugins, whose owner ids are the plugin ids the core's discovery
7 * builds (`<scope>/<hex>/<name>`); a builtin-tier host runs Codewhale's own
8 * host code, whose owner ids are `host:<module>`. The two id spaces cannot
9 * meet, and each process refuses an owner of the other tier
10 * (`HostRoot.activate`), so a core that mixed them up would be told so
11 * instead of running one tier's code in the other's process.
12 *
13 * Not a security boundary on its own (the OS sandbox and the separate
14 * processes are); the Rust core is the authority on what runs where.
15 */
16 export type { HostTier } from './protocol.generated.ts'
17 import type { HostTier } from './protocol.generated.ts'
18
19 /** Every tier-0 owner id starts with this. Mirrors `tier::HOST_OWNER_PREFIX` in Rust. */
20 export const HOST_OWNER_PREFIX = 'host:'
21
22 const TIERS: readonly string[] = ['plugin', 'builtin']
23
24 /**
25 * The tier named by `--tier=` in `argv` (the arguments after the script), or
26 * `plugin` when there is none: the least-privileged tier is the default for a
27 * host started by hand. An unknown value, a flag without a value, or a tier
28 * named twice throws, so the host refuses to start rather than guess.
29 */
30 export function parseTier(argv: readonly string[]): HostTier {
31 let found: HostTier | undefined
32 for (const arg of argv) {
33 if (arg !== '--tier' && !arg.startsWith('--tier=')) continue
34 const value = arg.startsWith('--tier=') ? arg.slice('--tier='.length) : ''
35 if (!TIERS.includes(value)) {
36 throw new Error(`unknown host tier ${JSON.stringify(value)} (expected --tier=plugin or --tier=builtin)`)
37 }
38 if (found !== undefined) throw new Error('the host tier was given more than once')
39 found = value as HostTier
40 }
41 return found ?? 'plugin'
42 }
43
44 /**
45 * The SHA-256 of each built-in module source this build embeds, in id order:
46 * what `host/hello` reports and the core checks against its own pinned table.
47 * The build (`build.mjs`) builds the modules first and substitutes their
48 * digests for `__BUILTIN_MODULE_DIGESTS__`; run from source it is empty.
49 */
50 declare const __BUILTIN_MODULE_DIGESTS__: Readonly<Record<string, string>>
51 export function builtinModuleDigests(): { id: string; sha256: string }[] {
52 const digests = typeof __BUILTIN_MODULE_DIGESTS__ === 'undefined' ? {} : __BUILTIN_MODULE_DIGESTS__
53 return Object.entries(digests)
54 .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
55 .map(([id, sha256]) => ({ id, sha256 }))
56 }
57
58 /** The tier an owner id belongs to. Total: the id decides. */
59 export function ownerTier(ownerId: string): HostTier {
60 return ownerId.startsWith(HOST_OWNER_PREFIX) ? 'builtin' : 'plugin'
61 }
62
62 lines TYPESCRIPT