1{{define "people"}}<section>2<h1>People</h1>3<h3>Members</h3>4<rpc-form method="listmembers"><button>List members</button></rpc-form>5<rpc-form method="setmember" compose="member">6<label>Pubkey <input name="member-pubkey" pattern="[0-9a-f]{64}" required></label>7<label>Name <input name="member-name"></label>8<label>Note <input name="member-note"></label>9<label>Role <select name="member-role"><option value="member">Member</option><option value="moderator">Moderator</option></select></label>10<button>Save member</button>11</rpc-form>12<rpc-form method="removemember"><label>Pubkey <input name="param" required></label><button>Remove member</button></rpc-form>13<rpc-form method="removesubtree"><label>Member pubkey <input name="param" required></label><button>Remove invite subtree</button></rpc-form>14{{if .Review}}<h3>Access requests</h3>15<p><small>People who asked to join without an invite. Approving adds them as members.</small></p>16{{with pendingRequests .Requests}}<table id="requests"><thead><tr><th>person</th><th>reason</th><th>asked</th><th></th></tr></thead><tbody>17{{range .}}{{$r := asMap .}}{{$pk := str (index $r "pubkey")}}<tr><td>{{template "nostrName" $pk}}</td><td>{{with str (index $r "reason")}}{{.}}{{else}}<small>no reason given</small>{{end}}</td><td><time datetime="{{datetime (index $r "requested_at")}}">{{short (index $r "requested_at")}}</time></td><td><rpc-form method="approvejoin" refresh><input type="hidden" name="param" value=""{{$pk}}""><button>Approve</button></rpc-form><rpc-form method="denyjoin" refresh><input type="hidden" name="param" value=""{{$pk}}""><button>Deny</button></rpc-form></td></tr>18{{end}}</tbody></table>{{else}}<p>No pending requests.</p>{{end}}19{{with decidedRequests .Requests}}<ul id="decided">{{range .}}{{$r := asMap .}}{{$status := str (index $r "status")}}<li data-state="{{$status}}">{{template "nostrName" (str (index $r "pubkey"))}} <small>{{$status}}{{with index $r "decided_at"}}{{if short .}} <time datetime="{{datetime .}}">{{short .}}</time>{{end}}{{end}}{{with str (index $r "decided_by")}} by {{template "nostrName" .}}{{end}}</small></li>{{end}}</ul>{{end}}20{{end}}<h3>Invites</h3>21<rpc-form method="createinvite">22<label>TTL seconds <input name="param" type="number" value="259200"></label>23<label>Maximum uses <input name="param" type="number" value="1"></label>24<label>Note <input name="param"></label>25<button>Create invite</button>26</rpc-form>27<rpc-form method="listinvites"><button>List invites</button></rpc-form>28<h3>Claims</h3>29<rpc-form method="listclaims"><button>List claims</button></rpc-form>30<rpc-form method="createclaim"><label>JSON claim <input name="param" value="{}"></label><button>Create claim</button></rpc-form>31<rpc-form method="deleteclaim"><label>Claim code <input name="param" required></label><button>Delete claim</button></rpc-form>32<h3>Lists</h3>33<rpc-form method="listallowedpubkeys"><button>Allowed pubkeys</button></rpc-form>34<rpc-form method="listbannedpubkeys"><button>Banned pubkeys</button></rpc-form>35</section>36{{end}}3738{{define "agents"}}<section>39<h1>Agents</h1>40<p>Agents are members with their own keys. Each one acts under a grant you sign, and stays the author of everything it does.</p>41{{template "alert" .Error}}42<table id="rows"><thead><tr><th>agent</th><th>scope</th><th>owner</th><th>state</th></tr></thead><tbody>43{{range .Feed}}{{$a := asMap .}}{{$pk := str (index $a "pubkey")}}<tr><td><a href="#agent-{{$pk}}">{{agentLabel $a}}</a></td><td title="{{agentScope $a}}">{{agentScope $a}}</td><td>{{template "nostrName" (str (index $a "owner"))}}</td><td data-state="{{agentState $a}}">{{agentState $a}}{{with agentSince $a}} | {{.}}{{end}}</td></tr>44{{else}}<tr><td colspan="4">No agents yet. Sign a grant below to add one.</td></tr>45{{end}}</tbody></table>46{{range .Feed}}{{$a := asMap .}}{{$pk := str (index $a "pubkey")}}{{$state := agentState $a}}{{$scope := asMap (index $a "scope")}}47<agent-card id="agent-{{$pk}}">48<h3>{{agentLabel $a}} <small><span data-state="{{$state}}">{{$state}}</span> | signed by {{if eq (str (index $a "owner")) $.Actor}}you{{else}}{{template "nostrName" (str (index $a "owner"))}}{{end}} | expires {{short (index $a "expires")}}</small></h3>49<table>50<tr><th>key</th><td><nostr-key hex="{{$pk}}" title="{{$pk}}">{{$pk}}</nostr-key> <small>click to copy</small></td></tr>51<tr><th>rooms</th><td>{{or (scopeList $a "rooms") "none"}}</td></tr>52<tr><th>repositories</th><td>{{range $i, $r := index $scope "repos"}}{{$repo := asMap $r}}{{if $i}}<br>{{end}}<code>{{str (index $repo "identifier")}}</code> {{str (index $repo "level")}}, owner {{template "nostrName" (str (index $repo "owner"))}}{{else}}none{{end}}</td></tr>53<tr><th>wiki</th><td>{{or (str (index $scope "wiki")) "none"}}</td></tr>54<tr><th>sites</th><td>{{range $i, $s := index $scope "sites"}}{{$site := asMap $s}}{{if $i}}<br>{{end}}<code>{{str (index $site "label")}}</code>{{with index $site "ttl"}} {{str .}} days{{end}}{{if index $site "encrypted"}} encrypted{{end}}{{else}}none{{end}}</td></tr>55<tr><th>kinds</th><td>{{or (scopeList $a "kinds") "profile and relay list only"}}</td></tr>56<tr><th>rate</th><td>{{str (index $scope "rate")}} events per minute</td></tr>57<tr><th>expires</th><td><time datetime="{{datetime (index $a "expires")}}">{{when (index $a "expires")}}</time></td></tr>58<tr><th>owner</th><td>{{template "nostrName" (str (index $a "owner"))}}</td></tr>59{{with index $a "lastEvent"}}{{if when .}}<tr><th>last event</th><td><time datetime="{{datetime .}}">{{when .}}</time></td></tr>{{end}}{{end}}60<tr><th>callbacks</th><td>{{range $c := callbacksFor $.Callbacks $pk}}{{$cm := asMap $c}}{{$cs := callbackState $c}}{{$cid := str (index $cm "id")}}<agent-callback data-state="{{$cs}}"><code>{{str (index $cm "host")}}</code> kinds {{callbackKinds $c}} | <span data-state="{{$cs}}" title="{{str (index $cm "lastStatus")}}">{{$cs}}</span>{{with index $cm "lastDelivery"}}{{if when .}} | delivered <time datetime="{{datetime .}}">{{when .}}</time>{{end}}{{end}} {{if eq $cs "paused"}}<rpc-form method="resumecallback" refresh><input type="hidden" name="param" value=""{{$cid}}""><button>Resume</button></rpc-form>{{else}}<rpc-form method="pausecallback" refresh><input type="hidden" name="param" value=""{{$cid}}""><button>Pause</button></rpc-form>{{end}}<rpc-form method="removecallback" refresh><input type="hidden" name="param" value=""{{$cid}}""><button>Remove</button></rpc-form></agent-callback>{{else}}none{{end}}</td></tr>61</table>62<p>{{if eq $state "paused"}}<rpc-form method="resumeagent" refresh><input type="hidden" name="param" value=""{{$pk}}""><button>Resume</button></rpc-form>{{else if eq $state "active"}}<rpc-form method="pauseagent" refresh><input type="hidden" name="param" value=""{{$pk}}""><button>Pause</button></rpc-form>{{end}}{{if ne $state "revoked"}}<rpc-form method="revokeagent" refresh><input type="hidden" name="param" value=""{{$pk}}""><button>Revoke</button></rpc-form>{{end}}<a href="/manage/agents?agent={{$pk}}">recent activity</a>{{if ne $state "revoked"}} <a href="/manage/agents?agent={{$pk}}&edit={{$pk}}#grant">edit</a>{{end}}</p>63</agent-card>64{{end}}{{with asMap .Event}}{{$agent := asMap (index . "agent")}}65<h3>Recent activity of {{agentLabel $agent}}</h3>66{{$events := index . "events"}}{{if $events}}<ol id="events">67{{range $events}}<li>{{template "nostrEvent" .}}</li>68{{end}}</ol>69{{else}}<p>No events from this agent yet.</p>70{{end}}{{end}}71{{$edit := agentByKey .Feed (.Query.Get "edit")}}{{$es := asMap (index (asMap $edit) "scope")}}<h3 id="grant">{{if $edit}}Replace the grant for {{agentLabel $edit}}{{else}}New agent{{end}}</h3>72<p>{{if $edit}}The form holds the current grant. Signing it publishes a new kind 30392 event for the same key, which replaces the old grant at once.{{else}}The grant is a kind 30392 event signed by your key. The agent's own key signs everything it publishes.{{end}}</p>73<agent-grant>74<label>Name <input name="name" maxlength="64" required placeholder="release-notes"{{with $edit}} value="{{agentLabel .}}"{{end}}></label>75<label>Key <select name="key"><option value="generate">generate here, show once</option><option value="paste"{{if $edit}} selected{{end}}>paste a public key</option></select></label>76<label>Public key <input name="pubkey" pattern="[0-9a-f]{64}" placeholder="64 hex characters"{{with $edit}} value="{{str (index (asMap .) "pubkey")}}"{{end}}></label>77<label>Rooms <input name="rooms" placeholder="build, agents"{{with $edit}} value="{{scopeList . "rooms"}}"{{end}}></label>78<label>Repositories <textarea name="repos" placeholder="owner pubkey:identifier:propose owner pubkey:identifier:read owner pubkey:identifier:maintain">{{with $edit}}{{repoLines .}}{{end}}</textarea></label>79<label>Wiki <select name="wiki"><option value="">none</option><option value="propose"{{if eq (str (index $es "wiki")) "propose"}} selected{{end}}>propose</option><option value="edit"{{if eq (str (index $es "wiki")) "edit"}} selected{{end}}>edit</option></select></label>80<label>Sites <textarea name="sites" placeholder="npub1... ttl=30 encrypted * ttl=7">{{with $edit}}{{siteLines .}}{{end}}</textarea></label>81<label>Kinds <input name="kinds" placeholder="1, 1111, 1621"{{with $edit}} value="{{scopeList . "kinds"}}"{{end}}></label>82<label>Rate per minute <input name="rate" type="number" min="1" max="600" value="{{if $edit}}{{str (index $es "rate")}}{{else}}60{{end}}"></label>83<label>Expires <input name="expires" type="date" value="{{if $edit}}{{dateOf (index (asMap $edit) "expires")}}{{else}}{{dateAfter 90}}{{end}}" required></label>84<button>{{if $edit}}Sign the replacement{{else}}Sign grant{{end}}</button>85<noscript><p>Signing a grant needs JavaScript and a connected signer. You can also publish the kind 30392 event from any client.</p></noscript>86</agent-grant>87</section>88{{end}}8990{{define "moderation"}}<section>91<h1>Moderation</h1>92<h3>Reports</h3>93<rpc-form method="listreports"><button>List reports</button></rpc-form>94<rpc-form method="resolvereport">95<label>Report ID <input name="param" required></label>96<label>JSON resolution <input name="param" value="{}"></label>97<button>Resolve report</button>98</rpc-form>99<h3>Events</h3>100<rpc-form method="listrecentevents"><button>Recent events</button></rpc-form>101<rpc-form method="searchevents"><label>JSON filter <input name="param" value="{}"></label><button>Search events</button></rpc-form>102<rpc-form method="banevent"><label>Event ID <input name="param" required></label><button>Hide event</button></rpc-form>103<rpc-form method="allowevent"><label>Event ID <input name="param" required></label><button>Allow event</button></rpc-form>104<rpc-form method="deleteevent"><label>Event ID <input name="param" required></label><button>Delete event</button></rpc-form>105<h3>Pins</h3>106<rpc-form method="listpins"><button>List pins</button></rpc-form>107<rpc-form method="pinevent"><label>Event ID or address <input name="param" required></label><button>Pin</button></rpc-form>108<rpc-form method="unpinevent"><label>Event ID or address <input name="param" required></label><button>Unpin</button></rpc-form>109<h3>Pubkeys</h3>110<rpc-form method="banpubkey"><label>Pubkey <input name="param" required></label><button>Ban</button></rpc-form>111</section>112{{end}}113114{{define "rules"}}<section>115<h1>Rules</h1>116<h3>Policy</h3>117<rpc-form method="getpolicy"><button>Load policy</button></rpc-form>118<rpc-form method="setpolicy" compose="memberInvites">119<label>Member invite depth <input name="member-invite-depth" type="number" min="0" max="100" value="0"></label>120<label>Live invites per member <input name="member-invite-quota" type="number" min="0" max="1000" value="0"></label>121<button>Save member invites</button>122</rpc-form>123<rpc-form method="setpolicy"><label>JSON policy patch <textarea name="param">{}</textarea></label><button>Apply policy</button></rpc-form>124<h3>Kinds</h3>125<rpc-form method="allowkind"><label>Kind <input name="param" type="number" required></label><button>Allow kind</button></rpc-form>126<rpc-form method="disallowkind"><label>Kind <input name="param" type="number" required></label><button>Block kind</button></rpc-form>127<h3>Retention</h3>128<rpc-form method="setretention">129<label>Kind or null <input name="param" value="null" required></label>130<label>Keep days <input name="param" type="number" min="0" required></label>131<button>Save retention</button>132</rpc-form>133</section>134{{end}}135136{{define "identity"}}<section>137<h1>Identity</h1>138<h3>Profile</h3>139<rpc-form method="changerelayname"><label>Name <input name="param" value="{{.Policy.Name}}"></label><button>Save name</button></rpc-form>140<rpc-form method="changerelaydescription"><label>Description <textarea name="param">{{.Policy.Description}}</textarea></label><button>Save description</button></rpc-form>141<rpc-form method="changerelayicon"><label>Icon URL <input name="param" type="url" maxlength="2000" value="{{.Policy.Icon}}" placeholder="https://…/icon.png"></label><button>Save icon</button></rpc-form>142<h3>Directory listing</h3>143<rpc-form method="setpolicy" compose="identity">144<label>Contact <input name="identity-contact" value="{{.Policy.Contact}}" type="email" placeholder="you@example.com"></label>145<label>Banner URL <input name="identity-banner" value="{{.Policy.Banner}}" type="url" placeholder="https://…/banner.png"></label>146<label>Posting policy URL <input name="identity-posting" value="{{.Policy.PostingPolicy}}" type="url" placeholder="https://…/posting"></label>147<label>Privacy policy URL <input name="identity-privacy" value="{{.Policy.PrivacyPolicy}}" type="url" placeholder="https://…/privacy"></label>148<label>Tags <input name="identity-tags" value="{{join .Policy.Tags ", "}}" placeholder="bitcoin, cats"></label>149<label>Languages <input name="identity-languages" value="{{join .Policy.LanguageTags ", "}}" placeholder="en, pt-BR"></label>150<label>Countries <input name="identity-countries" value="{{join .Policy.RelayCountries ", "}}" placeholder="US, DE"></label>151<button>Save directory identity</button>152</rpc-form>153<h3>Ownership</h3>154<rpc-form method="transferowner"><label>New owner pubkey <input name="param" required></label><button>Transfer ownership</button></rpc-form>155</section>156{{end}}157158{{define "connect"}}<section>159<h1>Connect</h1>160{{if .Private}}<p>This private relay requires current membership. Connect a signer to sign in.</p>161{{end}}<h3>Connections</h3>162<p>The cards on the home page, in order. Remove what you will not use; add more from the catalog. Cards whose feature is off stay hidden.</p>163<connect-list>164<table id="rows"><thead><tr><th>card</th><th>app</th><th>audience</th><th></th></tr></thead><tbody><tr><td colspan="4">Loading…</td></tr></tbody></table>165<p><label>Add a card <select name="template"><option value="">choose one</option></select></label> <button type="button" name="add">add</button></p>166<output role="status"></output>167</connect-list>168<details><summary>Edit as JSON</summary>169<rpc-form method="setconnections"><label>Connections <textarea name="param">[]</textarea></label><button>Save connections</button></rpc-form>170</details>171<h3>Publish signed relay records</h3>172<p>These actions open the configured signer and publish owner-authored Nostr records.</p>173<private-services pubkey="{{.Actor}}">174<h3>Private Git relay list</h3>175<p>GRASP-08 stores private relay URLs in your encrypted kind 10318 list. The relay URLs and any list entries are encrypted to your signer with NIP-44.</p>176<form><label>Private relay URLs, one per line <textarea name="lines" placeholder="wss://private-relay.example"></textarea></label><button disabled>Publish encrypted list</button> <button type="button" data-decrypt hidden>Decrypt to edit</button></form>177<noscript><p>Connect a signer and enable JavaScript to encrypt your private service list.</p></noscript>178<output role="status"></output>179</private-services>180<publish-list kind="10317" tag="g" scheme="ws">181<label>GRASP relay URLs, one per line <textarea name="lines" placeholder="wss://relay.example wss://backup.example"></textarea></label>182<button>Publish GRASP list</button>183</publish-list>184<publish-list kind="10063" tag="server">185<label>Blossom server URLs, one per line <textarea name="lines"></textarea></label>186<button>Publish server list</button>187</publish-list>188{{if not .Actor}}<h3>Sign in</h3>189{{template "signerControls" .}}190{{end}}</section>191{{end}}192193{{define "data"}}<section>194<h1>Data</h1>195<h3>Files</h3>196<signed-form action="/upload" method="PUT"><label>Upload file <input type="file" name="file" required></label><button>Upload</button></signed-form>197<rpc-form method="listblobs"><button>List blobs</button></rpc-form>198<rpc-form method="deleteblob"><label>Blob hash <input name="param" required></label><button>Delete blob</button></rpc-form>199<rpc-form method="listsites"><button>List sites</button></rpc-form>200<h3>Storage</h3>201<rpc-form method="storagestats"><button>Storage stats</button></rpc-form>202<rpc-form method="gitstorage">203<label>Repository owner <input name="param" required pattern="[0-9a-fA-F]{64}" placeholder="64 hex pubkey"></label>204<label>Repository identifier <input name="param" required maxlength="256" placeholder="repository name"></label>205<button>Git storage status</button>206</rpc-form>207<h3>Dumps</h3>208<rpc-form method="listdumps"><button>List dumps</button></rpc-form>209<rpc-form method="dumpnow"><button>Write dump</button></rpc-form>210<signed-form action="/dumps/" method="GET" download><label>Dump name <input name="name" required></label><button>Download dump</button></signed-form>211<rpc-form method="deletedump"><label>Dump name <input name="param" required></label><button>Delete dump</button></rpc-form>212<h3>Backups</h3>213<rpc-form method="listbackups"><button>List backups</button></rpc-form>214<rpc-form method="backupnow"><button>Create backup</button></rpc-form>215<signed-form action="/backups/" method="GET" download><label>Backup name <input name="name" required></label><button>Download backup</button></signed-form>216<signed-form action="/backups/preview" method="POST"><label>Backup archive <input type="file" name="file" accept=".backup.json,application/json" required></label><button>Preview backup</button></signed-form>217<signed-form action="/backups/restore" method="POST"><label>Backup archive <input type="file" name="file" accept=".backup.json,application/json" required></label><button>Restore backup</button></signed-form>218<rpc-form method="deletebackup"><label>Backup name <input name="param" required></label><button>Delete backup</button></rpc-form>219<h3>Import</h3>220<signed-form action="/import" method="PUT"><label>Event JSONL <input type="file" name="file" accept=".jsonl,.json,application/json" required></label><button>Import events</button></signed-form>221</section>222{{end}}223224{{define "sync"}}<section>225<h1>Sync and delivery</h1>226<h3>Jobs</h3>227<p><button id="watch-jobs" fx-action="/manage/jobs/status" fx-method="get" fx-trigger="click" fx-target="#job-status" fx-swap="innerHTML">Watch job status</button></p>228<scroll-box><table id="job-status"><tbody><tr><td>Job status is idle.</td></tr></tbody></table></scroll-box>229<rpc-form method="listjobs"><button>List jobs</button></rpc-form>230<rpc-form method="addjob"><label>JSON job <input name="param" value="{}"></label><button>Add job</button></rpc-form>231<rpc-form method="runjob"><label>Job ID <input name="param" required></label><button>Run job</button></rpc-form>232<rpc-form method="removejob"><label>Job ID <input name="param" required></label><button>Remove job</button></rpc-form>233<h3>Pull and delivery</h3>234<rpc-form method="pullfrom">235<label>Relay URL <input name="param" type="url" required></label>236<label>JSON filter <input name="param" value="{}"></label>237<button>Add pull source</button>238</rpc-form>239<rpc-form method="pullstatus"><button>Pull status</button></rpc-form>240<rpc-form method="deliverystatus"><button>Delivery status</button></rpc-form>241<rpc-form method="backfill"><label>JSON backfill plan <input name="param" value="{}"></label><button>Backfill</button></rpc-form>242</section>243{{end}}244245{{define "views"}}<section>246<h1>Views</h1>247<rpc-form method="listviews"><button>List views</button></rpc-form>248<rpc-form method="publishview"><label>View name <input name="param" required></label><button>Publish view</button></rpc-form>249{{if .Owner}}<h3>Block transforms</h3>250<p>Block transforms render fenced code as images. Choose the event kinds and languages to send to your rendering service.</p>251{{if .CustomViews}}<table id="custom-views">252<thead><tr><th>name</th><th>kinds</th><th>languages</th><th>trigger</th><th>audience</th><th>state</th><th>last run</th><th></th></tr></thead>253<tbody>{{range .CustomViews}}{{$v := asMap .}}<tr id="view-{{str (index $v "name")}}">254<td><code>{{str (index $v "name")}}</code></td>255<td>{{viewKinds $v}}</td>256<td>{{viewLanguages $v}}</td>257<td>{{str (index $v "trigger")}}</td>258<td>{{str (index $v "audience")}}</td>259<td data-state="{{viewState $v}}">{{viewState $v}}{{with str (index $v "lastStatus")}} | {{.}}{{end}}</td>260<td>{{with index $v "lastRun"}}{{when .}}{{else}}never{{end}}</td>261<td>{{if eq (viewState $v) "paused"}}<rpc-form method="resumecustomview" refresh><input type="hidden" name="param" value="{{str (index $v "name")}}"><button>Resume</button></rpc-form>{{else}}<rpc-form method="pausecustomview" refresh><input type="hidden" name="param" value="{{str (index $v "name")}}"><button>Pause</button></rpc-form><rpc-form method="runcustomview" refresh><input type="hidden" name="param" value="{{str (index $v "name")}}"><button>Run now</button></rpc-form><rpc-form method="runcustomview" refresh><input type="hidden" name="param" value='{"name":"{{str (index $v "name")}}","refresh":true}'><button>Rebuild</button></rpc-form>{{end}}<rpc-form method="removecustomview" refresh><input type="hidden" name="param" value="{{str (index $v "name")}}"><button>Remove</button></rpc-form></td>262</tr>{{end}}</tbody>263</table>{{else}}<p id="custom-views-empty">No custom views yet.</p>{{end}}264<view-form method="addcustomview">265<label>Name <input name="view-name" pattern="[a-z0-9-]{1,32}" placeholder="diagrams" required></label>266<label>Kinds <input name="view-kinds" placeholder="1, 30023, 30618" required></label>267<label>Languages <input name="view-languages" placeholder="mermaid, dot" required></label>268<label>Transform URL <input name="view-transform" type="url" placeholder="https://render.example/transform/blocks" required></label>269<label>Trigger <select name="view-trigger"><option value="write">write</option><option value="hourly">hourly</option></select></label>270<label>Audience <select name="view-audience"><option value="public">public</option><option value="members">members</option></select></label>271<label>Max artifact bytes <input name="view-max-bytes" type="number" min="1" max="4194304" placeholder="1048576"></label>272<label>Secret <input name="view-secret" minlength="16" maxlength="128" placeholder="generated when empty"></label>273<button>Add view</button>274</view-form>{{end}}275</section>276{{end}}277278{{define "health"}}<section>279<h1>Health</h1>280<table id="facts">281<tr><th>relay</th><td><code>{{or .Version "dev"}}</code>{{with .Revision}} <small>{{.}}</small>{{end}}</td></tr>282<tr><th>scripts</th><td><code>{{scriptsVersion}}</code></td></tr>283<tr><th>browser tools</th><td><span id="webmcp-status" role="status">Needs JavaScript to report.</span></td></tr>284</table>285<details><summary>About browser tools</summary><p>Browser agents use WebMCP tools registered on every page; Chrome's Application panel lists them and their calls. In Chrome, open <code>chrome://flags/#enable-webmcp-testing</code>, enable WebMCP for testing and relaunch. Your usual pages and signed forms work in other browsers.</p></details>286<script>(() => {287 const update = () => {288 const state = window.tinyWebMCP;289 const node = document.getElementById("webmcp-status");290 if (!node) return;291 node.textContent = !state?.supported ? "WebMCP is unavailable in this browser."292 : state.errors.length ? "Some tools could not register: " + state.errors.join("; ")293 : "ready, " + state.registered.length + " tools registered" + (state.lastOperation ? ", last: " + state.lastOperation : "");294 };295 window.addEventListener("tiny:webmcp", update);296 update();297 window.tinyWebMCP?.ready.then(update);298})();</script>299<h3>Service</h3>300{{if .Error}}{{template "alert" .Error}}{{else}}{{template "jsonView" .Event}}{{end}}301<p><a href="/manage/sync">Jobs and delivery</a> | <a href="/manage/data">Storage and backups</a></p>302<h3>Reports</h3>303<rpc-form method="stats"><button>Relay stats</button></rpc-form>304<rpc-form method="listaudit"><label>Before sequence <input name="param" type="number" value="0"></label><button>Audit log</button></rpc-form>305<rpc-form method="supportedmethods"><button>Supported methods</button></rpc-form>306</section>307{{end}}308309{{define "owner"}}<section>310<h1>Owner operations</h1>311<p>Destructive actions ask the signer. The browser never receives or submits a private key.</p>312<h3>Configuration</h3>313<rpc-form method="exportconfig"><button>Export configuration</button></rpc-form>314<rpc-form method="planconfig"><label>Config JSON <textarea name="param">{}</textarea></label><button>Preview configuration</button></rpc-form>315<rpc-form method="importconfig"><label>Config JSON <textarea name="param">{}</textarea></label><button>Apply configuration</button></rpc-form>316<rpc-form method="forkrelay"><label>Fork options JSON <textarea name="param">{}</textarea></label><button>Fork relay</button></rpc-form>317<h3>Succession</h3>318<rpc-form method="setsuccession" compose="succession">319<label>Heir pubkey <input name="succession-heir" pattern="[0-9a-f]{64}" required></label>320<label>Hand over after days <select name="succession-days"><option value="90">90</option><option value="180" selected>180</option><option value="365">365</option></select></label>321<button>Set heir</button>322</rpc-form>323<rpc-form method="clearsuccession"><button>Clear heir</button></rpc-form>324<h3>Delete</h3>325<rpc-form method="deleterelay"><label>Type relay name to confirm <input name="param" placeholder="{{.Slug}}" required></label><button>Delete relay</button></rpc-form>326<h3>Any management method</h3>327<p>Every retained NIP-86 method remains callable through the signed form bridge. Dedicated tabs supply guided fields for the common ones.</p>328<rpc-form json-params>329<label>Method <select name="method">{{range .Methods}}<option>{{.}}</option>{{end}}</select></label>330<label>JSON parameters <input name="param" value="[]"></label>331<button>Run method</button>332</rpc-form>333</section>334{{end}}