orchard
mirrorEvery site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.
blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics
1{{template "head" .}}
2
3{{if .Fresh}}
4<section class="fresh">
5 <p class="label">Copy this now</p>
6 <code class="secret">{{.Fresh}}</code>
7 <p class="note">Nothing here can show it again. It is stored as a SHA-256 and the plaintext was never written down.</p>
8</section>
9{{end}}
10
11<section class="stats">
12 <div class="stat"><span class="num">{{.NKeys}}</span><span class="label">Live keys</span></div>
13 <div class="stat"><span class="num">{{comma .NCalls}}</span><span class="label">Calls logged</span></div>
14 <div class="stat"><span class="num">{{.Retain}}</span><span class="label">Retention</span></div>
15</section>
16
17<h2>Last 24 hours</h2>
18{{if .Usage}}
19<table>
20 <thead><tr><th>Caller</th><th class="n">Calls</th><th class="n">Prompt</th><th class="n">Output</th><th class="n">Errors</th><th class="n">tok/s</th></tr></thead>
21 <tbody>
22 {{range .Usage}}
23 <tr>
24 <td><a href="/calls?caller={{.Caller}}">{{if .Caller}}{{.Caller}}{{else}}unknown{{end}}</a></td>
25 <td class="n">{{comma .Calls}}</td>
26 <td class="n">{{comma .PromptTok}}</td>
27 <td class="n">{{comma .OutputTok}}</td>
28 <td class="n {{if .Errors}}bad{{end}}">{{.Errors}}</td>
29 <td class="n">{{printf "%.1f" .AvgTPS}}</td>
30 </tr>
31 {{end}}
32 </tbody>
33</table>
34{{else}}
35<p class="empty">Nothing has called through yet.</p>
36{{end}}
37
38<h2>API keys</h2>
39<form class="newkey" method="post" action="/keys">
40 <input type="text" name="name" placeholder="What will use it, like chat or aiagent" required maxlength="60">
41 <button type="submit">Create key</button>
42</form>
43
44{{if .Keys}}
45<table>
46 <thead><tr><th>Name</th><th>Key</th><th>Created</th><th>Last used</th><th></th></tr></thead>
47 <tbody>
48 {{range .Keys}}
49 <tr {{if .Revoked}}class="off"{{end}}>
50 <td>{{.Name}}</td>
51 <td><code>{{.Prefix}}…</code></td>
52 <td class="faint">{{when .Created}}</td>
53 <td class="faint">{{when .Used}}</td>
54 <td class="act">
55 {{if .Revoked}}
56 <span class="tag">revoked</span>
57 <form method="post" action="/keys/{{.ID}}/delete"><button type="submit" class="danger">Delete</button></form>
58 {{else}}
59 <form method="post" action="/keys/{{.ID}}/revoke"><button type="submit">Revoke</button></form>
60 {{end}}
61 </td>
62 </tr>
63 {{end}}
64 </tbody>
65</table>
66{{else}}
67<p class="empty">No keys yet. Nothing can call the gateway until there is one.</p>
68{{end}}
69
70<h2>Recent calls</h2>
71{{if .Recent}}
72<table>
73 <thead><tr><th>When</th><th>Caller</th><th>Prompt</th><th class="n">Tokens</th><th class="n">ms</th></tr></thead>
74 <tbody>
75 {{range .Recent}}
76 <tr>
77 <td class="faint">{{when .At}}</td>
78 <td>{{.Caller}}</td>
79 <td class="msg">{{short .Messages 90}}</td>
80 <td class="n">{{comma .OutputTok}}</td>
81 <td class="n {{if .Err}}bad{{end}}">{{.MS}}</td>
82 </tr>
83 {{end}}
84 </tbody>
85</table>
86<p class="more"><a href="/calls">Every call</a></p>
87{{else}}
88<p class="empty">No calls yet.</p>
89{{end}}
90
91<h2>Using it</h2>
92<p class="how">Point any OpenAI shaped client at <code>http://orchard-llm:8000/v1</code> on the bridge, or <code>https://llm.bythewood.me/v1</code> from anywhere else, and send the key as <code>Authorization: Bearer orch-…</code>. Ask for the model named <code>{{.Model}}</code>. Upstream is <code>{{.Upstream}}</code>.</p>
93
94{{template "foot" .}}