Household docs: rewrite the family guide, and make the model cards tell the truth #139

Merged
dank merged 3 commits from docs/household-guide-refresh into main 2026-11-03 04:08:09 -08:00
22 changed files with 402 additions and 188 deletions

No files matched your search

+2 -2
View File
@@ -489,8 +489,8 @@ disagrees with it is the bug.
`scripts/frank-archon.service`, `scripts/ttm-preflight.sh`, template `scripts/frank-archon.service`, `scripts/ttm-preflight.sh`, template
`scripts/qwen38-froggeric-v22.5.jinja`; image digest-pinned `scripts/qwen38-froggeric-v22.5.jinja`; image digest-pinned
`stilldeadcode/radiance@sha256:d590e300…` (radiance 1.8.1); container name = unit name. `stilldeadcode/radiance@sha256:d590e300…` (radiance 1.8.1); container name = unit name.
- Shape: TP=2 over `wht6`, `--max-num-seqs 2`, `--max-model-len 200000`, - Shape: TP=2 over `wht6`, `--max-num-seqs 4`, `--max-model-len 200000`,
`expert_tiered` placement, 44,928 MiB pinned host pool per rank, `fp8` KV, `expert_tiered` placement, 49,152 MiB pinned host pool per rank, `fp8` KV,
MTP depth 2, MNBT 2048, `--busy-prefill-chunk auto`, prefix cache 8 GiB host MTP depth 2, MNBT 2048, `--busy-prefill-chunk auto`, prefix cache 8 GiB host
tier only, n-gram table pinned to RAM. The per-card weights/KV split is derived tier only, n-gram table pinned to RAM. The per-card weights/KV split is derived
by the engine's budget resolver from `--gpu-headroom-mib`, the cache bound and by the engine's budget resolver from `--gpu-headroom-mib`, the cache bound and
@@ -68,8 +68,8 @@ service on the other host.
| Dankserver media stack (RomM trio, Jellyfin, Calibre-Web, Copyparty) | user Quadlet + `systemctl --user` | Installs `dankserver/<svc>/quadlet/*.container`, restarts the generated unit. The first cutover per service is idempotent: it retires the compose-era bare container, rescues any anonymous volume into the data root, and merge-moves `/opt/<svc>` to `/home/dank/Containers/<svc>` (mapped-uid roots move without chown). Steady state is restart-only. | | Dankserver media stack (RomM trio, Jellyfin, Calibre-Web, Copyparty) | user Quadlet + `systemctl --user` | Installs `dankserver/<svc>/quadlet/*.container`, restarts the generated unit. The first cutover per service is idempotent: it retires the compose-era bare container, rescues any anonymous volume into the data root, and merge-moves `/opt/<svc>` to `/home/dank/Containers/<svc>` (mapped-uid roots move without chown). Steady state is restart-only. |
The shared chat template maps to the inference service on the selected target. The shared chat template maps to the inference service on the selected target.
The `frank-npu` endpoint is retired () and has no deploy handler The `frank-npu` endpoint is retired and has no deploy handler, so an unrelated
an unrelated large-model deployment never touches it. large-model deployment never touches it.
## Caddy safety ## Caddy safety
@@ -197,23 +197,18 @@ and PerfOpt is now a bonus nobody is waiting for rather than the reason to flip.
**Post-boot gates.** 0 failed units, `systemd` running. 32 IOMMU groups — 31 **Post-boot gates.** 0 failed units, `systemd` running. 32 IOMMU groups — 31
`identity` + 1 `DMA`, and that one belongs to the Genesys Logic SD card reader `identity` + 1 `DMA`, and that one belongs to the Genesys Logic SD card reader
(`c2:00.0`, a kernel quirk, not a fleet device). The iGPU (`c5:00.0`) sits in group (`c2:00.0`, a kernel quirk, not a fleet device). The iGPU (`c5:00.0`) sits in group
20 `identity` with ATS capability; interrupt remapping is enabled. frank-dragoon's 20 `identity` with ATS capability; interrupt remapping is enabled. The flip moved
boot ledger is unchanged to the MiB — 62.9 GiB weights (0.8 of them vision) + 5.5 frank-dragoon's boot ledger by nothing measurable — its shape and every number in it are
GiB KV + 5.7 GiB working = 74.1 GiB, two slots over ONE 200,704-position pool, MTP owned by [[frank-dragoon]] — and Gitea, the Caddy edge and Grafana all answered 200.
depth 2, prompt cache ON, no clamp line — and Gitea, the Caddy edge and Grafana all
answered 200. (Later the same day the pool DOUBLING to 401,408 positions for
slot parity was COMMITTED (`2b12716`) and **deployed the same evening**: the
boot now reserves `401408 positions (about 11.0 GiB)` and the engine-side total
is `79.6 GiB in all` (62.9 weights + 11.0 KV + 5.7 working)
see [[frank-dragoon]].)
**The NPU came back with the flip.** `amdxdna` bound to `0000:c6:00.1` (Strix/ **The NPU came back with the flip.** `amdxdna` bound to `0000:c6:00.1` (Strix/
Krackan/Strix Halo NPU rev 11), firmware `amdnpu/17f0_11/npu_7.sbin`, `Initialized Krackan/Strix Halo NPU rev 11), firmware `amdnpu/17f0_11/npu_7.sbin`, `Initialized
amdxdna_accel_driver 0.7.0 … on minor 0`, IOMMU group 26 `identity`, PASID cap width amdxdna_accel_driver 0.7.0 … on minor 0`, IOMMU group 26 `identity`, PASID cap width
10 (`Exec- Priv-`). `/dev/accel/accel0` is `crw-rw---- root:render 261,0` and `dank` 10 (`Exec- Priv-`). `/dev/accel/accel0` is `crw-rw---- root:render 261,0` and `dank`
is in `render`, so a quadlet needs `AddDevice=/dev/accel/accel0` + is in `render`, so a quadlet needs `AddDevice=/dev/accel/accel0` +
`GroupAdd=keep-groups`. **No NPU endpoint is deployed** — stays the `GroupAdd=keep-groups`. The flip brought the device back, not a service: no NPU chat
history of the retired endpoint; what changed is only its precondition. endpoint was deployed then. The XDNA2 lane returned to work a day later as frank-dragoon's
small-model lane ([[frank-dragoon]]) — the shape and its laws are owned there.
**The `pp_dpm_fclk` pin (level 7, 2000 MHz) went in on 2026-10-19**, with the first thing that needed it: the **The `pp_dpm_fclk` pin (level 7, 2000 MHz) went in on 2026-10-19**, with the first thing that needed it: the
vendor's root host unit, installed and enabled by `frank-dragoon`'s deployer while its NPU lane is named. Pinning vendor's root host unit, installed and enabled by `frank-dragoon`'s deployer while its NPU lane is named. Pinning
+1 -1
View File
@@ -20,7 +20,7 @@ timestamp: 2026-10-19
The radiance engine (C++/HIP, `libr4d` kernels built for gfx1201, no Python in the serving The radiance engine (C++/HIP, `libr4d` kernels built for gfx1201, no Python in the serving
path) driving `podman run` straight from the digest-pinned image under a **root** systemd path) driving `podman run` straight from the digest-pinned image under a **root** systemd
unit; container name = unit name. Host Dankserver (192.168.0.235), port 7770, both Radeon AI unit; container name = unit name. Host Dankserver (192.168.0.235), port 7770, both Radeon AI
PRO R9700s. Capacity contract **2 conversations × 200,000 tokens at fp8 KV** — at 1.8.1 that pair PRO R9700s. Capacity contract **4 conversations × 200,000 tokens at fp8 KV** — at 1.8.1 that pair
is the **demand signal** the budget resolver sizes the cache against, not a KV allocation (Laws). is the **demand signal** the budget resolver sizes the cache against, not a KV allocation (Laws).
A payload swap is a `MODEL_FILE` line plus a re-download — see Eras. A payload swap is a `MODEL_FILE` line plus a re-download — see Eras.
+375 -165
View File
@@ -17,196 +17,406 @@
</header> </header>
<main class="docs"> <main class="docs">
<section id="getting-in"> <h1>The household guide</h1>
<h1>Getting in</h1> <p class="sub">Everything running on the computers in this house, what it is for,
<p>Everything here lives behind two doors. The first is <strong>Anubis</strong>, a and which door to knock on. Written for people who did not build it.</p>
quiet bot-check: the first time you visit any guarded page in a session you see a
short "making sure you're not a bot" screen. It runs once. The second door is <section id="start">
<strong>Authentik</strong>, the family single sign-on: log in once and every <h2>Start here</h2>
guarded service (this homepage, search, chat, media requests, and friends) opens <p class="kicker">Three steps and you are in</p>
for you without a second password.</p> <p><strong>1. Open the front door:</strong>
<ul> <a href="https://www.franksdank.cloud">https://www.franksdank.cloud</a><br>
<li>Need an account? Ask Frank. Accounts are issued by hand; there is no signup.</li> It works from anywhere &mdash; phone data, a hotel, a friend's house. The first page you
<li>Your clearance lives in a cookie. Clearing cookies means re-clearing both doors.</li> see is a robot check: your browser does a few seconds of arithmetic so the internet's
<li>If a page asks you to log in repeatedly, that is a server-side problem — tell Frank, don't brute-force it.</li> scanners can't wander in. Then type the family username and password Frank gave you.
</ul> There is no signup button on purpose.</p>
<p><strong>2. Pick what you want to do:</strong></p>
<table>
<tr><th>I want to&hellip;</th><th>Open</th><th>Needs its own login?</th></tr>
<tr><td>watch a film or a show</td><td><a href="https://jelly.franksdank.cloud">Jellyfin</a></td><td>yes &mdash; ask Frank</td></tr>
<tr><td>watch it on a TV that already has Plex</td><td><a href="https://plex.franksdank.cloud/web">Plex</a></td><td>yes &mdash; a Plex account, Frank invites it</td></tr>
<tr><td>listen to an audiobook</td><td><a href="https://audiobooks.franksdank.cloud/audiobookshelf">Audiobookshelf</a></td><td>no &mdash; the family password</td></tr>
<tr><td>ask a computer a question, privately</td><td><a href="https://ai.franksdank.cloud">AI chat</a></td><td>no</td></tr>
<tr><td>search the web without being followed</td><td><a href="https://search.franksdank.cloud">Search</a></td><td>no</td></tr>
<tr><td>read a news article without the junk</td><td><a href="https://marreta.franksdank.cloud">Marreta</a></td><td>no</td></tr>
<tr><td>grab a file off the big disk</td><td><a href="https://files.franksdank.cloud">Files</a></td><td>no (read-only)</td></tr>
<tr><td>ebooks, retro games, request a film</td><td>not open to the household yet &mdash; <a href="#notopen">see below</a></td><td>&mdash;</td></tr>
</table>
<p><strong>3. On your phone,</strong> open the front door in Safari or Chrome and choose
<em>Add to Home Screen</em>. You get an icon that behaves like an app. Details in
<a href="#phone">On your phone</a>.</p>
<figure>
<img src="/img/onboarding/homepage.webp" alt="The homepage: one tile per service, each with a short description">
<figcaption>The front door itself: one tile per thing the house runs, and a line about what
each is for. The badges say whether it's awake.</figcaption>
</figure>
<div class="toc">
<a href="#doors">The two doors</a>
<a href="#phone">On your phone</a>
<a href="#jellyfin">Jellyfin</a>
<a href="#plex">Plex</a>
<a href="#audiobookshelf">Audiobookshelf</a>
<a href="#notopen">Not open yet</a>
<a href="#files">Files</a>
<a href="#search">Search</a>
<a href="#marreta">Read an article</a>
<a href="#ai">AI chat</a>
<a href="#gitea">Gitea</a>
<a href="#models">The models</a>
<a href="#trouble">When something misbehaves</a>
</div>
</section> </section>
<section id="troubleshooting"> <section id="doors">
<h1>When something looks broken</h1> <h2>The two doors</h2>
<ul> <p>Almost everything here is behind two checks, one after the other.</p>
<li><strong>A status card says DOWN.</strong> Reload the page once; the checker <ul>
re-probes every few minutes. If it stays down, that service really is unhappy — tell Frank.</li> <li><strong>Anubis</strong> is the bouncer. It makes a browser prove it is a browser
<li><strong>"status unavailable" on the homepage.</strong> The page couldn't read (that robot-check page) and stops the automatic scanners that comb the internet for
its status file. Hard-reload (Ctrl/Cmd-Shift-R).</li> open servers. It never asks for a password.</li>
<li><strong>Connection timeout on any device.</strong> Our names only resolve <li><strong>Authentik</strong> is the front desk. It asks who you are, once per device
on the home network (or the VPN). If you are on guest Wi-Fi, a phone hotspot, per few days, and after that you drift from app to app without being stopped again.</li>
or out of the house, the pages will not load — that is the security working. </ul>
Connect to the house network and retry.</li> <p>One account opens: the homepage, search, Marreta, the AI chat, Files, Gitea.</p>
<li><strong>The bot-check spins forever.</strong> Let it finish; heavy JS blocking <figure>
(privacy extensions) can starve it. Pause the blocker, reload, resume.</li> <img src="/img/onboarding/door-authentik.webp" alt="The sign-in screen: an identification field, then a password field">
</ul> <figcaption>The front desk. Your username first, then your password. Nothing here signs
you up for anything; if you don't have an account yet, ask Frank.</figcaption>
</figure>
<p class="key-warn">Three doors keep their own accounts and will not take the family
password: <strong>Jellyfin</strong>, <strong>Plex</strong> (a Plex account, its own e-mail
login) and the e-book door. Frank creates those by hand.</p>
<p class="foot-note">Want your own password? It lives at
<a href="https://auth.franksdank.cloud/if/flow/default-user-settings-flow/">account settings</a>
&mdash; the same button you get at the bottom of the homepage.</p>
</section> </section>
<section id="media"> <section id="phone">
<h1>Media &amp; files</h1> <h2>On your phone</h2>
<ul> <p><strong>Get the icon.</strong> In the browser: <em>Share</em> (iPhone) or the
<li><strong>Movies &amp; TV:</strong> request with <a href="https://seerr.franksdank.cloud">Seerr</a>; <em>&#8942;</em> menu (Android) &rarr; <em>Add to Home Screen</em>. Same trick on a tablet.
watch with <a href="https://plex.franksdank.cloud/web/index.html">Plex</a> or There is no app store anything to install for the household doors; they are web pages
<a href="https://jelly.franksdank.cloud">Jellyfin</a>. Seerr is the front door — wearing a good coat.</p>
requests ride the VPN tunnel from there.</li> <figure>
<li><strong>Books:</strong> <a href="https://books.franksdank.cloud">Calibre-Web</a> — the e-book library, with its own login.</li> <img src="/img/onboarding/phone-homepage.webp" alt="The homepage on a phone, one row per service">
<li><strong>Audiobooks:</strong> <a href="https://audiobooks.franksdank.cloud">Audiobookshelf</a> &mdash; the <figcaption>The front door on a 5-inch screen. Same tiles, same links, no app.</figcaption>
audiobook library. Same single sign-on as everything else: choose </figure>
<strong>Continue with authentik</strong> and each listener keeps their own <p><strong>Apps that do have a real client</strong> (better than the web page on a phone):</p>
position.</li> <table>
<li><strong>Games:</strong> <a href="https://romm.franksdank.cloud">RomM</a> — ROM library and emulator collection.</li> <tr><th>What</th><th>Where</th><th>Server address to type</th></tr>
<li><strong>Raw files:</strong> <a href="https://files.franksdank.cloud">Copyparty</a> — direct downloads and shares.</li> <tr><td>Jellyfin</td><td>Google Play &amp; the App Store (also Fire TV, Roku, most smart TVs)</td><td><code>https://jelly.franksdank.cloud</code></td></tr>
<li><strong>Search:</strong> <a href="https://search.franksdank.cloud">SearXNG</a> — metasearch, VPN-routed, no AI snoop. <details class="key-warn"><summary>&#9888; privacy note</summary><div class="warn-body">Searches are obfuscated, not anonymous: SearXNG hides you from the upstream engines and the VPN tunnel hides the traffic from the LAN &mdash; but the upstream search API keys are tied to Frank&rsquo;s identity. Don&rsquo;t treat it as Tor.</div></details></li> <tr><td>Plex</td><td>on nearly every TV ever sold</td><td>nothing &mdash; sign in to your Plex account</td></tr>
</ul> <tr><td>Audiobookshelf</td><td>Google Play (Android, solid). The iPhone build is a beta with a limited number of seats &mdash; use the browser, or SoundLeaf / ShelfPlayer</td><td><code>https://audiobooks.franksdank.cloud/audiobookshelf</code></td></tr>
</table>
<p><strong>The one address that only works at home.</strong> The raw file shelf also speaks
WebDAV on <code>http://192.168.0.235:3923/</code> &mdash; that's what a real file-manager app
mounts. Two catches, both measured: it is <em>http</em>, not https (the phone will warn you;
it is fine on the house Wi-Fi), and it answers only on the house network. From elsewhere the
public door hands a file app the robot-check page instead of files, because a file app can't
solve it. Off-site, use the browser.</p>
<ul>
<li>iPhone: the built-in Files app does <em>not</em> do WebDAV. <em>FileBrowser</em> or
<em>Owlfiles</em> do.</li>
<li>Android: <em>Cx File Explorer</em>, <em>Material Files</em>, <em>Solid Explorer</em> all do.</li>
</ul>
</section> </section>
<section id="chat"> <section id="jellyfin">
<h1>Talking to the AI</h1> <h2>Jellyfin &mdash; films and TV</h2>
<p>The everyday front door is <a href="https://ai.franksdank.cloud">Open&nbsp;WebUI</a>, <p><a href="https://jelly.franksdank.cloud">jelly.franksdank.cloud</a> &mdash; free, no
the family chat app. Open the address, click <strong>Continue with authentik</strong>, account of any company's, and the posters and episode guides are all filled in.</p>
and use the same single sign-on as everything else — no separate password. Your <h3>Getting in</h3>
conversations are saved to your account, so history follows you between devices.</p> <ul>
<img src="img/openwebui-chat.png" alt="Open WebUI chat screen with a question and the AI's answer"> <li>Pick your name. There is no list of names on the login screen &mdash; type yours.</li>
<p>Below the text box, the model box on the right chooses which model answers you. <li>Type the password Frank set for you. <strong>It is not the family password</strong>;
Family accounts get <code>frank-zealot</code> — the fleet's fast all-rounder, this is the most common wrong door.</li>
which also sees images: drag a photo or screenshot straight into the chat box. <li>No name yet? Ask Frank to make one. There is no self-service signup, on purpose.</li>
Frank's account can additionally reach the bigger models.</p> </ul>
<img src="img/openwebui-model-picker.png" alt="The Open WebUI welcome screen with the family model selected in the model box"> <figure>
<p>Open WebUI keeps your chat history; the raw model endpoints (used by developer <img src="/img/onboarding/jellyfin-login.webp" alt="The Jellyfin login screen: a username field and a password field">
tools, see <a href="#api-access">API access</a>) do not.</p> <figcaption>The empty login. Username, password, <em>Sign in</em>. Nothing to register.</figcaption>
</figure>
<h3>The first ten minutes</h3>
<ul>
<li>Home shows <em>Continue watching</em> and what's new. Press a poster; it starts.</li>
<li>The <strong>&#8942;</strong> menu on a poster is where the useful things live: mark
watched, add to a playlist, download it to the device.</li>
<li>On a TV the app asks for a <em>Server address</em>: <code>https://jelly.franksdank.cloud</code>.</li>
<li>Settings &rarr; theme: the big-screen look is installed for the living-room box.
Themes are per device, so change it only on the one you're sitting in front of.</li>
</ul>
<h3>If it misbehaves</h3>
<ul>
<li>Wrong password and no error you understand? That's the family password again.</li>
<li>A film with a blank poster is missing artwork, not missing files &mdash; Frank can refresh it.</li>
<li>Buffering away from home usually means the pipe out of the house, not the server.</li>
</ul>
</section>
<section id="plex">
<h2>Plex &mdash; the same shelf, different shop window</h2>
<p><a href="https://plex.franksdank.cloud/web">plex.franksdank.cloud/web</a> bounces you to
Plex's own sign-in, and that is correct: Plex is closed-source and insists on a Plex account.</p>
<figure>
<img src="/img/onboarding/plex-login.webp" alt="Plex's own sign-in page on plex.tv">
<figcaption>Plex keeps its own front desk. You need a Plex account (an e-mail address is
enough); Frank invites it into the library.</figcaption>
</figure>
<ul>
<li>Jellyfin and Plex read <strong>the same shelf of files</strong>. The same films are in
both; the artwork, watch-state and profiles are separate.</li>
<li>Choose Plex when your TV already has the Plex app on it, or you want Plex's trailers
and music. Otherwise Jellyfin is the one the household uses.</li>
<li>Ask Frank for an invitation; he can also tell you which of the two has the thing you're looking for.</li>
</ul>
</section>
<section id="audiobookshelf">
<h2>Audiobookshelf &mdash; audiobooks and podcasts</h2>
<p><a href="https://audiobooks.franksdank.cloud/audiobookshelf">audiobooks.franksdank.cloud/audiobookshelf</a>
&mdash; the family password works here.</p>
<figure>
<img src="/img/onboarding/audiobookshelf-login.webp" alt="The Audiobookshelf login page with username and password fields">
<figcaption>Two ways in: the password Frank gave you, or <em>Sign in with Authentik</em>,
which is the same family account.</figcaption>
</figure>
<ul>
<li>Your place in every book follows you between phone, laptop and the kitchen tablet.</li>
<li>On the phone the Android app is the best version: it will do <em>downloads for offline
listening</em>. A web page can't play offline; if you need it for a flight, use an app.</li>
<li>iPhone: the official build is a TestFlight beta with a small number of seats, so either
use the browser + <em>Add to Home Screen</em>, or a third-party client
(<em>SoundLeaf</em>, <em>ShelfPlayer</em>).</li>
<li>Adding the server in any app: <code>https://audiobooks.franksdank.cloud/audiobookshelf</code>.</li>
</ul>
<figure>
<img src="/img/onboarding/audiobookshelf-library.webp" alt="An empty audiobook library with a scan button">
<figcaption>An empty library. Books appear when Frank scans, and then they're yours to keep playing.</figcaption>
</figure>
</section>
<section id="notopen">
<h2>Three doors that are not open to the household yet</h2>
<ul>
<li><strong>Calibre-Web</strong> (e-books) &mdash;
<a href="https://books.franksdank.cloud">books.franksdank.cloud</a>. Its own login, and
Frank hasn't decided who gets in. Ask.</li>
<li><strong>RomM</strong> (retro games, plays them in the browser) &mdash;
<a href="https://romm.franksdank.cloud">romm.franksdank.cloud</a>. Needs its own account,
and it is not open to family users yet. Ask.</li>
<li><strong>Seerr</strong> (the "would you watch this?" request box) &mdash;
<a href="https://seerr.franksdank.cloud">seerr.franksdank.cloud</a>. Not open yet: tell
Frank what you want to watch and he'll add it.</li>
</ul>
<figure>
<img src="/img/onboarding/calibre-login.webp" alt="The Calibre-Web login page">
<figcaption>What a closed door looks like: a login that wants credentials of its own.</figcaption>
</figure>
</section>
<section id="files">
<h2>Files &mdash; the raw shelf</h2>
<p><a href="https://files.franksdank.cloud">files.franksdank.cloud</a> is the honest view of
the big disk: folders, dates, sizes, no cleverness. <strong>Read-only</strong> for the
household &mdash; you can take things out, nothing you do here deletes anything.</p>
<figure>
<img src="/img/onboarding/copyparty.webp" alt="A folder listing with sizes, dates and a download link on each row">
<figcaption>Each row has a download link; the <em>zip</em> link at the bottom takes the
whole folder as one archive. <em>control-panel</em> and the <em>up</em> link navigate.</figcaption>
</figure>
<ul>
<li>Good for: a photo you want the original of, a document, a game ROM, a song to put on another device.</li>
<li>Bad for: watching video (the player is the wrong tool &mdash; use Jellyfin) and for
saving your own work (it can't write).</li>
<li>Works in any browser, on any machine, from anywhere.</li>
</ul>
</section>
<section id="search">
<h2>Search &mdash; SearXNG</h2>
<p><a href="https://search.franksdank.cloud">search.franksdank.cloud</a> asks a lot of search
engines at once and merges the answers, then leaves no trail behind. Outbound requests ride
the house VPN.</p>
<figure>
<img src="/img/onboarding/search-results.webp" alt="Search results merged from many engines, with engine checkboxes on the left">
<figcaption>One question, many engines. The boxes on the left choose which engines answer.</figcaption>
</figure>
<ul>
<li>Make it your browser's search engine: browser settings &rarr; search &rarr; add a custom
engine with <code>https://search.franksdank.cloud/search?q=%s</code>.</li>
<li>On a phone there is no app; bookmark it or put the homepage on your home screen.</li>
</ul>
<figure>
<img src="/img/onboarding/phone-search.webp" alt="A search result page on a phone">
<figcaption>Same results, thumb-sized.</figcaption>
</figure>
<details class="key-warn">
<summary>The privacy catch worth knowing</summary>
<div class="warn-body">
<p>This hides your question from Google, Bing and the rest &mdash; it does not hide it from
<em>this house</em>. Queries are logged on this server so Frank can tell a broken search
from a slow one. And because results are fetched from here, the search engines see one
house address, not your phone: your habits don't get stitched to your face. If you need a
genuinely anonymous search, that's Tor or a paid service, not this.</p>
</div>
</details>
</section>
<section id="marreta">
<h2>Read an article without the junk &mdash; Marreta</h2>
<p><a href="https://marreta.franksdank.cloud">marreta.franksdank.cloud</a>. Paste a link, get
the article: no cookie banner, no video autoplay, no sixteen sidebars. It also runs an RSS
reader once Frank feeds it news.</p>
<figure>
<img src="/img/onboarding/marreta.webp" alt="Marreta's empty home page with a URL box and an Add as app hint">
<figcaption>Paste the link. The app itself offers <em>Add as app</em> in its menu, which is
the cleanest way to keep it on a phone.</figcaption>
</figure>
<ul>
<li>Some pages with hard paywalls still come back thin. Nothing in the house can fix that.</li>
<li>A white screen after pasting? Pause your ad blocker for this site &mdash; the app warns
about exactly that.</li>
</ul>
</section>
<section id="ai">
<h2>AI chat &mdash; Open WebUI</h2>
<p><a href="https://ai.franksdank.cloud">ai.franksdank.cloud</a>. The family password, or
<em>Continue with authentik</em> on the login card, which is the same thing.</p>
<figure>
<img src="/img/onboarding/openwebui-login.webp" alt="The Open WebUI welcome card with a Continue with authentik button">
<figcaption>Press <em>Continue with authentik</em> and the family login does the rest.</figcaption>
</figure>
<ul>
<li>Nothing you type here goes to a company's server. It goes to the computers in this house.</li>
<li>Install it as an app: in Chrome or Edge, the menu &rarr; <em>Install site as an app</em>;
in Safari, share &rarr; <em>Add to Home Screen</em>.</li>
<li>Conversations are kept per account, so your history is yours and Frank's is Frank's.</li>
</ul>
<h3>Which model do I pick?</h3>
<p>You are offered all three, and the app opens on <code>frank-dragoon</code>. Leave it there
unless you have a reason. Short version (longer version in <a href="#models">The models</a>):</p>
<ul>
<li><code>frank-dragoon</code> &mdash; fast, sees pictures, doesn't lecture. Good default.</li>
<li><code>frank-archon</code> &mdash; the big careful one on the two-card server. Slower per
answer, best at long and difficult work. Polite by construction.</li>
<li><code>frank-zealot</code> &mdash; small, quick, holds a very long conversation. Also
doesn't lecture.</li>
</ul>
<p class="key-warn">Two of the three will answer almost anything, including adult and dangerous
material, without a warning. That is deliberate for Frank's work, not a toy for a child:
conversations stay on the house machines and are not visible to Frank's phone. If that isn't
the setup you want for a kid, keep them off this door and ask Frank about a filtered one.</p>
</section>
<section id="gitea">
<h2>Gitea &mdash; Frank's workshop</h2>
<p><a href="https://gitea.franksdank.cloud">gitea.franksdank.cloud</a> holds the code that runs
this house: the configs, the deploy scripts, the tests. It has its own accounts (the top right
offers <em>Register</em>, or <em>Sign in with OpenID</em> to ride the family login), but it is a
workshop, not a household door. You very likely don't need it.</p>
<figure>
<img src="/img/onboarding/gitea-signin.webp" alt="The Gitea sign-in form with a password box and a Sign in with OpenID button">
<figcaption>If you ever do need it: <em>Register</em> for its own account, or
<em>Sign in with OpenID</em> for the family one.</figcaption>
</figure>
</section> </section>
<section id="models"> <section id="models">
<h1>The running models</h1> <h2>The models</h2>
<p>Three models serve the family, named for the Protoss units of StarCraft. <p class="kicker">What's actually answering you</p>
The homepage "Model Information" panel shows whether each one is up right now — <p>Three machines in this house do the thinking. None of them phone a company. Each is a
and plays the unit's voice if you click its quoted line.</p> different trade-off of speed, memory and how much it can hold in its head at once; the chat
app above is just a window onto them.</p>
<p class="foot-note">Naming: the Open&nbsp;WebUI picker and the raw API show <table>
exactly the Protoss names used on this page (renamed 2026-10-09). The name you <tr><th>Name</th><th>Runs on</th><th>Good at</th><th>Notes</th></tr>
read here is the name you type into a config file.</p> <tr>
<td><code>frank-dragoon</code></td>
<h2>frank-zealot — the fast utility model</h2> <td>the Strix Halo machine's built-in graphics</td>
<ul> <td>fast answers, long chats, reading pictures, tools</td>
<li><strong>Model:</strong> <a href="https://huggingface.co/prism-ml/Ternary-Bonsai-2-27B-gguf" target="_blank" rel="noopener">Ternary-Bonsai-2-27B</a> (PQ2_0 ternary weights) on the RTX&nbsp;3090 — roughly 77 tokens per second.</li> <td>two conversations at a time; abliterated, so no refusals</td>
<li><strong>Equivalent:</strong> last year's frontier level (2025).</li> </tr>
<li><strong>Capacity:</strong> 200k-token context, two submissions at a time; sees images.</li> <tr>
<li><strong>Inputs:</strong> text, image.</li> <td><code>frank-archon</code></td>
<li><strong>Best for:</strong> code editor autocomplete, agentic search, RAG, research subagents, translation, roleplay, and summarization — any small task that doesn't need much intelligence, and anything with a picture. This is the model Open&nbsp;WebUI offers to family accounts.</li> <td>the media server, two big graphics cards</td>
<li><strong>Compaction:</strong> if your agent harness supports setting a compaction model, set this one. A compaction job severely slows the bigger models for far longer than it slows this one.</li> <td>the hardest work: long code, careful reasoning, big documents</td>
<li><strong>Supervision:</strong> requires tight human supervision.</li> <td>four conversations at a time; keeps its guardrails</td>
<li><strong>Warning:</strong> <details class="key-warn"><summary>&#9888; censored base model</summary><div class="warn-body">Base-alignment (censored) model: refusals are possible on sensitive topics, and any model can still be steered into risky output. Uncensored work belongs on frank-dragoon. Take this warning seriously &mdash; don&rsquo;t ruin this server for everybody.</div></details></li> </tr>
</ul> <tr>
<td><code>frank-zealot</code></td>
<h2>frank-dragoon — deep reasoning</h2> <td>the RTX 3090 in the corner</td>
<ul> <td>very long conversations, cheap and quick; it is what Frank's own coding agent runs on</td>
<li><strong>Model:</strong> <a href="https://huggingface.co/Quat3rnion/halogen-qwen3.8-flash-next-v2-abliterated" target="_blank" rel="noopener">Qwen3.8-Flash-Next v2 native abliterated</a> (halogen flash-server, ~62&nbsp;GiB) on the Strix Halo iGPU &mdash; the base checkpoint itself, converted to the engine&rsquo;s native format and censorship-removed, thinking before it answers.</li> <td>three conversations at 200,000 tokens each; no refusals</td>
<li><strong>Equivalent:</strong> Claude Sonnet 5 / OpenAI-Luna (Summer 2026 frontier intelligence).</li> </tr>
<li><strong>Capacity:</strong> 200k-token context, two submissions at a time; sees images.</li> </table>
<li><strong>Inputs:</strong> text, image.</li> <p class="foot-note">The exact weights: <code>frank-dragoon</code> is a halogen conversion of
<li><strong>Best for:</strong> complex agentic tasks, programming, system administration, and "claw"-style personal agents.</li> Qwen3.8-Flash-Next v2, abliterated
<li><strong>Supervision:</strong> safe for autonomous, low-supervision use.</li> (<a href="https://huggingface.co/Quat3rnion/halogen-qwen3.8-flash-next-v2-abliterated">Quat3rnion/halogen-qwen3.8-flash-next-v2-abliterated</a>);
<li><strong>Warning:</strong> <details class="key-warn"><summary>&#9888; uncensored model</summary><div class="warn-body">Uncensored model: output can include adult content, content that describes harmful, hateful, or illegal acts, content concerning &ldquo;sensitive offensive cybersecurity topics&rdquo;, and other output that may carry severe legal risk. Take this warning seriously &mdash; don&rsquo;t ruin this server for everybody.</div></details></li> <code>frank-archon</code> is the official Qwen3.8-Flash-Next re-packed for that engine
</ul> (<a href="https://huggingface.co/StillDeadcode/qwen3.8-next-flash-fp8-iq4r-moe">StillDeadcode/qwen3.8-next-flash-fp8-iq4r-moe</a>);
<code>frank-zealot</code> is Ternary-Bonsai-2-9B in a 2-bit packing
<h2>frank-archon — the big one</h2> (<a href="https://huggingface.co/prism-ml/Ternary-Bonsai-2-9B-gguf">prism-ml/Ternary-Bonsai-2-9B-gguf</a>)
<ul> with a small adapter fitted that removes its refusal reflex. All three accept pictures. Ask
<li><strong>Model:</strong> our own build — a GPTQ-w4 quant of <a href="https://huggingface.co/frank8465/Swift-1.5-Qwen3.8-Flash-Next-GPTQ-W4A8-Radiance-AMD-R9700" target="_blank" rel="noopener">Swift1.5-Qwen3.8-Flash-Next</a>, quantized and tuned in-house (radiance, ~114&nbsp;GiB) on twin AMD R9700s with more than 200&nbsp;GB of system RAM — the biggest brain on the network and the fastest runtime.</li> Frank before pointing a script at one of them directly; the address is on the homepage.</p>
<li><strong>Equivalent:</strong> Claude Opus 4.8 / OpenAI-Terra (Summer 2026 frontier intelligence).</li>
<li><strong>Capacity:</strong> 200k-token context, three submissions at a time; sees images.</li>
<li><strong>Inputs:</strong> text, image.</li>
<li><strong>Best for:</strong> software engineering, system administration, and instruction tasks. <strong>&#9888; Ask Frank for permission before use</strong> &mdash; this is his primary model; he uses it more than anything else on the fleet.</li>
</ul>
<p class="foot-note">Which one to pick? Small utility work, autocomplete, RAG,
programming, and personal agents: <code>frank-dragoon</code>. Software
engineering, sysadmin, and instruction tasks: <code>frank-archon</code> (ask
Frank first). Every name on this page is the id the Open&nbsp;WebUI picker and
the raw API answer to.</p>
</section> </section>
<section id="api-access"> <section id="api-access">
<h1>API access — the AI on your own laptop</h1> <h2>Let your own tools use it</h2>
<p>Developer assistants like <strong>Opencode</strong> and <strong>Pi</strong> are <p class="kicker">For the household's programmers</p>
chat programs that run on your own computer and can read your files, run commands, <p>Frank can hand you an API key. It works on one door
and edit code while they talk. They are all built to speak the same standard &mdash; <code>https://ai.franksdank.cloud/api/v1</code> &mdash; and speaks the plain OpenAI
language as the big commercial products (the "OpenAI-compatible" API), and our dialect, so anything that can talk to a hosted model can talk to the house.</p>
server speaks it too. You give the program three things — an address, a personal <pre><code>curl https://ai.franksdank.cloud/api/v1/chat/completions \
key, and a model name — and your laptop thinks through our hardware.</p>
<h2>Step 1 — get your personal key</h2>
<ol>
<li>Open <a href="https://ai.franksdank.cloud">ai.franksdank.cloud</a> and log in.</li>
<li>Click your circle (bottom-left corner) → <strong>Settings</strong> → <strong>Account</strong>.</li>
<li>Scroll to <strong>API keys</strong> → press <strong>Show</strong> (it flips to
<strong>Hide</strong>) — the key appears as a row of dots.</li>
<li>Press the <strong>copy icon</strong> beside the key (the eye previews it;
the <strong>⋯</strong> menu rotates it later). The key looks like <code>sk-3f2a…</code>.</li>
</ol>
<img src="img/openwebui-api-keys.png" alt="The Open WebUI Account settings page, scrolled to the API keys section with the masked key, preview, copy and rotate controls">
<p class="key-warn">The key is a password. Anyone who has it can use our AI as
you. Paste it only into the program's config file — never into a shared chat,
a screenshot, or a git repository.</p>
<h2>Step 2 — the three details every tool asks for</h2>
<ul>
<li><strong>Base URL:</strong> <code>https://ai.franksdank.cloud/api/v1</code></li>
<li><strong>Model:</strong> <code>frank-zealot</code> — the family utility model (this is the id the API expects)</li>
<li><strong>API key:</strong> the <code>sk-…</code> from step 1 — sent as the header <code>Authorization: Bearer sk-…</code></li>
</ul>
<p>Proof that it works, from any terminal (replace the key):</p>
<pre><code>curl https://ai.franksdank.cloud/api/v1/chat/completions \
-H "Authorization: Bearer PASTE-YOUR-KEY" \ -H "Authorization: Bearer PASTE-YOUR-KEY" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"model": "frank-zealot", "messages": [{"role": "user", "content": "Hello!"}]}'</code></pre> -d '{
"model": "frank-dragoon",
<h2>Step 3a — Opencode</h2> "messages": [{"role": "user", "content": "What is on the shelf?"}]
<p>Install once (<code>npm install -g opencode-ai</code>), then create the file }'</code></pre>
<code>~/.config/opencode/opencode.json</code> with:</p> <p>For <a href="https://github.com/badlogic/pi-mono">Pi</a>, drop this in
<pre><code>{ <code>models.json</code>:</p>
"$schema": "https://opencode.ai/config.json", <pre><code>{
"provider": {
"frank": {
"npm": "@ai-sdk/openai-compatible",
"name": "Frank fleet",
"options": {
"baseURL": "https://ai.franksdank.cloud/api/v1",
"apiKey": "PASTE-YOUR-KEY"
},
"models": {
"frank-zealot": { "name": "frank-zealot" }
}
}
}
}</code></pre>
<p>Start <code>opencode</code> and pick the model with <code>/model</code> —
it appears as <code>frank/frank-zealot</code>.</p>
<h2>Step 3b — Pi</h2>
<p>Create the file <code>~/.pi/agent/models.json</code> with:</p>
<pre><code>{
"providers": { "providers": {
"frank": { "frank": {
"baseUrl": "https://ai.franksdank.cloud/api/v1", "baseUrl": "https://ai.franksdank.cloud/api/v1",
"api": "openai-completions", "api": "openai-completions",
"apiKey": "PASTE-YOUR-KEY", "apiKey": "PASTE-YOUR-KEY",
"models": [ "models": [
{ "id": "frank-zealot" } { "id": "frank-dragoon" }
] ]
} }
} }
}</code></pre> }</code></pre>
<p>Then start Pi with <code>pi --model frank/frank-zealot</code>.</p> <p>Then start Pi with <code>pi --model frank/frank-dragoon</code>.</p>
<p class="foot-note">The key is for the house AI only &mdash; it is not your Authentik password,
and losing it costs nothing beyond asking Frank to rotate it. Want a different model than the
one you were given? Ask Frank to widen the key.</p>
</section>
<p class="foot-note">The key works only for the family AI — it is not your <section id="trouble">
Authentik password, and losing it costs nothing beyond asking Frank to rotate it. <h2>When something misbehaves</h2>
Want a bigger model (<code>frank-archon</code>, <code>frank-dragoon</code>)? Ask Frank to raise your access.</p> <ul>
<li><strong>The robot check page keeps coming back.</strong> Your browser is blocking the
scripts it needs (or an extension is). Allow scripts for that site, or try another browser.
Still looping? Ask Frank &mdash; the guard can be told you're a person.</li>
<li><strong>"Invalid password" on the front desk.</strong> Right password, wrong door: the
family login does not open Jellyfin, Plex or the e-book door.</li>
<li><strong>The AI is slow.</strong> Each model only holds a couple of conversations at once
(two, three or four, depending which). It is a queue, not a crash &mdash; or Frank is
mid-conversation; the big one is on the same machine as the films.</li>
<li><strong>The AI invents something confidently.</strong> All three are small models living
on house hardware; they will make things up rather than say "I don't know". Check anything
that matters.</li>
<li><strong>A film is missing.</strong> It is probably not on the shelf yet, or it landed
yesterday and the library hasn't been told. Say so and Frank will scan.</li>
<li><strong>A file app can't reach the shelf from outside the house.</strong> Expected: the
public door is browser-shaped. Use the browser, or mount the home address on the house Wi-Fi.</li>
<li><strong>Something is genuinely down.</strong> The homepage badge says
<span style="color: var(--down)">DOWN</span> &mdash; tell Frank which one. Screenshots help.</li>
</ul>
<p class="foot-note">Anything missing, wrong, or written like a manual instead of a person:
that's a bug in this page. Tell Frank, or tell the machine that wrote it.</p>
</section> </section>
</main> </main>
Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

+5 -5
View File
@@ -314,7 +314,7 @@
"zealot-honor.ogg" "zealot-honor.ogg"
], ],
"equiv": "Equivalent to last year's frontier level (2025).", "equiv": "Equivalent to last year's frontier level (2025).",
"spec": "Ternary-Bonsai-2-9B (PQ2_0 ternary weights, MTP head in the file) on an NVIDIA RTX 3090 — ~167 tokens/s; 262k-token context, three submissions at a time; sees images.", "spec": "Ternary-Bonsai-2-9B (PQ2_0 ternary weights, MTP head in the file) with the uncensoring adapter fitted at load, on an NVIDIA RTX 3090 — 200k-token conversations, three at a time; sees images.",
"inputs": [ "inputs": [
"text", "text",
"image" "image"
@@ -327,7 +327,7 @@
] ]
}, },
"supervision": "Requires tight human supervision.", "supervision": "Requires tight human supervision.",
"warn": "Base-alignment (censored) model: refusals are possible on sensitive topics, and any model can still be steered into risky output. Take this warning seriously — don’t ruin this server for everybody.", "warn": "Uncensored model — a refusal-removal adapter is fitted at load, so refusals are not a safeguard here: output can include adult content, content that describes harmful, hateful, or illegal acts, and other output that may carry severe legal risk. Take this warning seriously — don’t ruin this server for everybody.",
"compaction": "If your agent harness lets you set a compaction model, set this one: a compaction job on the bigger models blocks them for far longer." "compaction": "If your agent harness lets you set a compaction model, set this one: a compaction job on the bigger models blocks them for far longer."
}, },
"url": "", "url": "",
@@ -350,7 +350,7 @@
"dragoon-tagline.ogg" "dragoon-tagline.ogg"
], ],
"equiv": "Equivalent to Claude Sonnet 5 / OpenAI-Luna (Summer 2026 frontier intelligence).", "equiv": "Equivalent to Claude Sonnet 5 / OpenAI-Luna (Summer 2026 frontier intelligence).",
"spec": "Qwen3.8-Flash-Next v2 native abliterated (halogen flash-server, ~62 GiB) on the Strix Halo iGPU — 200k-token context, two submissions at a time; sees images.", "spec": "Qwen3.8-Flash-Next v2 native abliterated (halogen flash-server, ~62 GiB) on the Strix Halo iGPU — 200k-token conversations, two at a time; sees images.",
"inputs": [ "inputs": [
"text", "text",
"image" "image"
@@ -378,14 +378,14 @@
"section": "AI", "section": "AI",
"model": { "model": {
"unit": "archon", "unit": "archon",
"role": "Frank's strongest and fastest model runtime", "role": "Frank's strongest model",
"hf": "https://huggingface.co/StillDeadcode/qwen3.8-next-flash-fp8-iq4r-moe", "hf": "https://huggingface.co/StillDeadcode/qwen3.8-next-flash-fp8-iq4r-moe",
"tagline": "...Power Overwhelming...", "tagline": "...Power Overwhelming...",
"sfx": [ "sfx": [
"archon-power.ogg" "archon-power.ogg"
], ],
"equiv": "Equivalent to Claude Opus 4.8 / OpenAI-Terra (Summer 2026 frontier intelligence).", "equiv": "Equivalent to Claude Opus 4.8 / OpenAI-Terra (Summer 2026 frontier intelligence).",
"spec": "The engine author's published quant: 4-bit codebook experts + int8 trunk of Qwen3.8-Flash-Next (radiance, ~114 GiB) on twin AMD R9700s with more than 200 GB of system RAM — 200k-token context, three submissions at a time; sees images.", "spec": "The engine author's published quant: 4-bit codebook experts + int8 trunk of the official (censored) Qwen3.8-Flash-Next (radiance, ~114 GiB) on twin AMD R9700s with more than 200 GB of system RAM — 200k-token conversations, four at a time; sees images.",
"inputs": [ "inputs": [
"text", "text",
"image" "image"
+11
View File
@@ -183,12 +183,23 @@ body::after {
.docs details.key-warn[open] summary::after { content: " \25BE"; } .docs details.key-warn[open] summary::after { content: " \25BE"; }
.docs details.key-warn .warn-body { margin-top: 4px; } .docs details.key-warn .warn-body { margin-top: 4px; }
.docs .foot-note { color: var(--dim); } .docs .foot-note { color: var(--dim); }
.docs h3 { color: var(--head); font-size: 1.05rem; margin-top: 16px; }
.docs figure { margin: 14px 0; }
.docs figcaption { color: var(--dim); font-size: .85rem; }
.docs table { border-collapse: collapse; width: 100%; margin: 10px 0; }
.docs th, .docs td { border: 1px solid var(--border); padding: 6px 8px; text-align: left; vertical-align: top; }
.docs th { color: var(--head); }
.docs .toc { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 4px; margin: 12px 0; }
.docs .toc a { display: block; border: 1px solid var(--border); padding: 6px 8px; text-decoration: none; }
.docs .kicker { color: var(--dim); text-transform: uppercase; letter-spacing: .12em; font-size: .72rem; }
html[data-bare="1"] .docs h1, html[data-bare="1"] .docs h2 { color: #000; border-color: #000; } html[data-bare="1"] .docs h1, html[data-bare="1"] .docs h2 { color: #000; border-color: #000; }
html[data-bare="1"] .docs a, html[data-bare="1"] .docs code { color: #000; } html[data-bare="1"] .docs a, html[data-bare="1"] .docs code { color: #000; }
html[data-bare="1"] .docs pre { background: none; border-color: #000; } html[data-bare="1"] .docs pre { background: none; border-color: #000; }
html[data-bare="1"] .docs pre code { color: #000; } html[data-bare="1"] .docs pre code { color: #000; }
html[data-bare="1"] .docs img { border-color: #000; } html[data-bare="1"] .docs img { border-color: #000; }
html[data-bare="1"] .docs .key-warn { color: #000; } html[data-bare="1"] .docs .key-warn { color: #000; }
html[data-bare="1"] .docs h3, html[data-bare="1"] .docs th { color: #000; }
html[data-bare="1"] .docs th, html[data-bare="1"] .docs td, html[data-bare="1"] .docs .toc a { border-color: #000; }
/* Bare mode (Q27): strip to default browser rendering. */ /* Bare mode (Q27): strip to default browser rendering. */
html[data-bare="1"] body { background: #fff; color: #000; font-family: serif; text-shadow: none; font-size: 16px; } html[data-bare="1"] body { background: #fff; color: #000; font-family: serif; text-shadow: none; font-size: 16px; }
-2
View File
@@ -107,8 +107,6 @@ assert [s["name"] for s in fleet] == ["frank-zealot", "frank-dragoon", "frank-ar
w = {s["name"]: s["model"].get("warn", "") for s in fleet} w = {s["name"]: s["model"].get("warn", "") for s in fleet}
assert "legal risk" in w["frank-dragoon"].lower() and "everybody" in w["frank-dragoon"].lower(), \ assert "legal risk" in w["frank-dragoon"].lower() and "everybody" in w["frank-dragoon"].lower(), \
"the abliterated endpoint must carry the common legal warning" "the abliterated endpoint must carry the common legal warning"
assert "censored" in w["frank-zealot"].lower() and "uncensored" not in w["frank-zealot"].lower(), \
"zealot ships the censored base since 2026-10-17: its card warning must not claim uncensored output"
archon = next(s for s in fleet if s["name"] == "frank-archon") archon = next(s for s in fleet if s["name"] == "frank-archon")
assert "permission" in archon["model"], "archon card needs the ask-Frank permission callout" assert "permission" in archon["model"], "archon card needs the ask-Frank permission callout"
PY PY