secret — example zut extension
Demonstrates secret collection via a masked panel: the model asks
for a resource that needs a credential, the extension collects the
credential directly from the user inside a panel, uses it to perform
the operation, and returns only the outcome to the model. The secret
is never written to any JSON frame or the transcript.
What it does
Registers one LLM-callable tool:
fetch_with_password(url: string)
When the model calls it, a masked password panel opens:
╭─ Password required ─────────────────────╮
│ URL: https://internal.example.com │
│ │
│ Password: ●●●●●●●▌ │
╰──────────────────────────────────────────╯
type password enter confirm esc cancel
The panel re-renders after every keystroke, showing bullets instead of
characters. Pressing Enter unblocks the tool goroutine, which uses the
password directly (in doFetch) and returns only the fetch outcome to
the model.
Security property
The password lives only in the extension process's memory. It is never
serialised into a JSON frame, never appears in the transcript, and
never reaches the model. The model sees:
fetched https://internal.example.com successfully (password was 9 characters, not shown)
Build
cd examples/extensions/secret
go build -o secret .
Install
zut ext install .
Try it
In zut, ask:
Fetch https://internal.example.com/report — it needs a password.
The model calls fetch_with_password; the masked panel opens. Type
anything and press Enter. The model receives the result; the
password is gone.
Adapting to real use
Replace doFetch in main.go with a real HTTP request:
func doFetch(url, password string) ext.ToolResult {
req, _ := http.NewRequest("GET", url, nil)
req.SetBasicAuth("user", password)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return ext.TextErrorResult("fetch failed: " + err.Error())
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
return ext.TextResult(string(body))
}
The same pattern works for any credential type: API tokens, SSH
passphrases, TOTP codes, or freeform override strings.
See also
examples/extensions/approve — same pattern for approve/deny gates
(no text input, just y/n)
docs/extensions.md — full protocol reference
source
1# secret — example zut extension
2
3Demonstrates **secret collection via a masked panel**: the model asks
4for a resource that needs a credential, the extension collects the
5credential directly from the user inside a panel, uses it to perform
6the operation, and returns only the outcome to the model. The secret
7is never written to any JSON frame or the transcript.
8
9## What it does
10
11Registers one LLM-callable tool:
12
13```
14fetch_with_password(url: string)
15```
16
17When the model calls it, a masked password panel opens:
18
19```
20╭─ Password required ─────────────────────╮
21│ URL: https://internal.example.com │
22│ │
23│ Password: ●●●●●●●▌ │
24╰──────────────────────────────────────────╯
25 type password enter confirm esc cancel
26```
27
28The panel re-renders after every keystroke, showing bullets instead of
29characters. Pressing Enter unblocks the tool goroutine, which uses the
30password directly (in `doFetch`) and returns only the fetch outcome to
31the model.
32
33## Security property
34
35The password lives only in the extension process's memory. It is never
36serialised into a JSON frame, never appears in the transcript, and
37never reaches the model. The model sees:
38
39```
40fetched https://internal.example.com successfully (password was 9 characters, not shown)
41```
42
43## Build
44
45```bash
46cd examples/extensions/secret
47go build -o secret .
48```
49
50## Install
51
52```bash
53zut ext install .
54```
55
56## Try it
57
58In zut, ask:
59
60> Fetch https://internal.example.com/report — it needs a password.
61
62The model calls `fetch_with_password`; the masked panel opens. Type
63anything and press **Enter**. The model receives the result; the
64password is gone.
65
66## Adapting to real use
67
68Replace `doFetch` in `main.go` with a real HTTP request:
69
70```go
71func doFetch(url, password string) ext.ToolResult {
72 req, _ := http.NewRequest("GET", url, nil)
73 req.SetBasicAuth("user", password)
74 resp, err := http.DefaultClient.Do(req)
75 if err != nil {
76 return ext.TextErrorResult("fetch failed: " + err.Error())
77 }
78 defer resp.Body.Close()
79 body, _ := io.ReadAll(resp.Body)
80 return ext.TextResult(string(body))
81}
82```
83
84The same pattern works for any credential type: API tokens, SSH
85passphrases, TOTP codes, or freeform override strings.
86
87## See also
88
89- `examples/extensions/approve` — same pattern for approve/deny gates
90 (no text input, just y/n)
91- `docs/extensions.md` — full protocol reference