Skip to main content

packc/
component_host_stubs.rs

1#![forbid(unsafe_code)]
2
3use anyhow::Result;
4use wasmtime::component::Linker;
5use wasmtime_wasi::p2::add_to_linker_sync;
6use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView};
7use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView};
8use wasmtime_wasi_tls::{
9    LinkOptions as WasiTlsLinkOptions, WasiTls, WasiTlsCtx, WasiTlsCtxBuilder,
10};
11
12pub struct DescribeHostState {
13    table: ResourceTable,
14    wasi: WasiCtx,
15    wasi_http: WasiHttpCtx,
16    wasi_tls: WasiTlsCtx,
17}
18
19impl Default for DescribeHostState {
20    fn default() -> Self {
21        // Describe-only paths should be offline-safe: no inherited env/args,
22        // no preopened directories, and no ambient stdio requirements.
23        let mut wasi = WasiCtxBuilder::new();
24        Self {
25            table: ResourceTable::new(),
26            wasi: wasi.build(),
27            wasi_http: WasiHttpCtx::new(),
28            wasi_tls: WasiTlsCtxBuilder::new().build(),
29        }
30    }
31}
32
33impl WasiView for DescribeHostState {
34    fn ctx(&mut self) -> WasiCtxView<'_> {
35        WasiCtxView {
36            table: &mut self.table,
37            ctx: &mut self.wasi,
38        }
39    }
40}
41
42impl WasiHttpView for DescribeHostState {
43    fn ctx(&mut self) -> &mut WasiHttpCtx {
44        &mut self.wasi_http
45    }
46
47    fn table(&mut self) -> &mut ResourceTable {
48        &mut self.table
49    }
50}
51
52pub fn add_describe_host_imports(linker: &mut Linker<DescribeHostState>) -> Result<()> {
53    // Some WASI helper registrars may re-export overlapping interface names
54    // (for example `wasi:io/*`) across preview2, TLS, and HTTP worlds.
55    linker.allow_shadowing(true);
56
57    add_to_linker_sync(linker)
58        .map_err(|err| anyhow::anyhow!("register wasi preview2 describe host stubs: {err}"))?;
59
60    let mut tls_options = WasiTlsLinkOptions::default();
61    tls_options.tls(true);
62    wasmtime_wasi_tls::add_to_linker(linker, &mut tls_options, |host: &mut DescribeHostState| {
63        WasiTls::new(&host.wasi_tls, &mut host.table)
64    })
65    .map_err(|err| anyhow::anyhow!("register wasi tls describe host stubs: {err}"))?;
66
67    wasmtime_wasi_http::add_only_http_to_linker_sync(linker)
68        .map_err(|err| anyhow::anyhow!("register wasi http describe host stubs: {err}"))?;
69
70    // NOTE: greentic host interfaces (state-store, secrets-store, http-client,
71    // interfaces-types, etc.) are NOT registered here. They are handled by
72    // `stub_remaining_imports` which uses `define_unknown_imports_as_traps` to
73    // provide trap stubs for any component imports not already in the linker.
74    // This avoids having to maintain an exhaustive list of every greentic
75    // interface version a component might import.
76    Ok(())
77}
78
79/// Stub any component imports not already registered in the linker as traps.
80///
81/// This must be called AFTER `add_describe_host_imports` so that real WASI
82/// implementations take priority. All remaining imports (greentic host
83/// interfaces like secrets-store, http-client, etc.) become traps — safe
84/// because the describe-only code path never invokes them.
85pub fn stub_remaining_imports(
86    linker: &mut Linker<DescribeHostState>,
87    component: &wasmtime::component::Component,
88) -> Result<()> {
89    linker
90        .define_unknown_imports_as_traps(component)
91        .map_err(|err| anyhow::anyhow!("stub remaining component imports: {err}"))
92}