User Manual
Everything you need to run Krebf on your WordPress site. (v1.0.9)
About this software
Krebf is a JavaScript port of Fredrik Ramsberg's Ruby Z-machine interpreter. The original Ruby source lives at github.com/fredrikr/krebf and is the authoritative reference for any interpreter behaviour question.
Contact:
- Original Krebf (Ruby): Fredrik Ramsberg — via the GitHub repository above.
- JavaScript port & WordPress plugin: Bill Martens — via callapple.org/krebf-support.
Contents
1. Installation
From the plugin zip
- Download
krebf.zip. - In WordPress: Plugins → Add New → Upload Plugin.
- Choose the zip, click Install Now, then Activate.
Manual install via SFTP
- Upload the
krebf/folder into/wp-content/plugins/on your server. - Go to Plugins → Installed Plugins and activate Krebf Z-Machine Player.
What activation does
- Creates
wp-content/uploads/krebf-games/if it doesn't already exist. - Drops a silent
index.phpinto that folder so nobody can browse its listing. - Writes a deny-all
.htaccessso direct HTTP access to the raw story files is refused — the player gets them via the REST endpoint, which the plugin fully controls. - Registers defaults for the four plugin options.
2. Adding story files
The runtime location for your story files is:
wp-content/uploads/krebf-games/
Drop your .z3, .z5, .z8 (etc.) files
in that directory using SFTP, SSH, or your hosting provider's file manager.
The filename becomes the slug:
| File on disk | URL slug | Dropdown label |
|---|---|---|
zork1.z3 | zork1 | Zork 1 |
hitchhikers.z5 | hitchhikers | Hitchhikers |
a-mind-forever-voyaging.z4 | a-mind-forever-voyaging | A Mind Forever Voyaging |
trinity.z4 | trinity | Trinity |
Supported extensions
.z1 .z2 .z3 .z4 .z5 .z7 .z8 .dat .story — anything else
is ignored when listing and refused when requested. Maximum file size is
512 KB (Z-machine spec's hard ceiling for v8 stories).
3. The [krebf] shortcode
Drop this into any post, page, or block-based layout:
[krebf]
That gives you the full player: welcome screen, games dropdown, upload button, save/restore/transcript controls. The visitor picks a game, or you link to the page with a URL parameter (see next section).
Attributes
| Attribute | Default | Purpose |
|---|---|---|
game |
(none) | A slug from your games folder, or an https://… URL.
Auto-loads on page open. Overridden by a ?game=
URL parameter if the visitor arrives with one. |
height |
720px |
CSS height for the player container. Accepts px,
em, rem, %, vh,
vw, ch. |
width |
100% |
CSS width. Same unit set as height. |
Examples
[krebf]
[krebf game="zork1"]
[krebf game="zork1" height="800px"]
[krebf game="https://example.com/cloak.z5"]
One per page
The interpreter uses module-level state — only one player runs
per page load. If you place [krebf] twice, the first copy
is active and the second becomes inert scenery.
4. URL parameters
On any page containing the shortcode, you can preselect the story with:
https://your-site.example/play/?game=zork1
https://your-site.example/play/?game=trinity
https://your-site.example/play/?game=https%3A%2F%2Fifarchive.org%2F...
Two forms are recognised:
- A slug — a plain word (letters, numbers, hyphens, underscores, dots). It's resolved against your games folder server-side. The visitor never sees the absolute path on disk.
- An
https://…URL — only if external URLs are enabled in Settings → Krebf. The plugin proxies the download through your server to sidestep CORS, checks the host against your allow-list, rejects private IP ranges (SSRF defence), caps the response at 512 KB, and verifies the first byte is a valid Z-machine version number.
Priority
When multiple sources say "load a game", Krebf picks in this order:
(1) the ?game= URL parameter,
(2) the shortcode's game="" attribute,
(3) the site-wide default set in admin settings.
5. The games dropdown
In the top-right of the player sits a select list labelled "— choose a story —". Krebf populates it automatically by scanning your games folder. Pretty titles are derived from the filenames (underscores and dashes become spaces, words are capitalised).
Because the dropdown is filled server-side from the already-sanitised file list, visitors only ever see filenames and human-readable titles; the absolute server path never leaves the host.
Refreshing the list
Add or remove files in the games folder and the next page load will reflect it. There is no caching layer to bust; the plugin scans the directory on each render.
6. Themes
As of v1.0.1, the player ships with four selectable visual themes:
- Amber CRT (default). Deep black background, amber phosphor text, scanlines, brass-and-cream UI chrome. The nostalgic-Infocom look.
- Green phosphor (VT100). The same retro treatment but tuned to the green of a VT100 or Apple ½-Mac. Scanlines and vignette still present.
- Paperwhite. A light-mode reader theme — serif body, warm cream background, walnut-brown text, no CRT effects. Suited to literary sites that want the interpreter to read like a book page.
- Inherit from current site theme. Borrows the
host theme’s computed text colour, background, and font
stack via
currentColorandcolor-mix(). No scanlines, no hard-coded palette. Drop it in and it becomes part of the page.
Pick the site-wide default under Settings → Krebf →
Appearance. Override per-embed with the theme=""
shortcode attribute:
[krebf theme="green"]
[krebf theme="paperwhite" game="zork1"]
[krebf theme="inherit"]
When the chosen theme is Inherit, Krebf doesn't enqueue Google Fonts — useful on privacy-conscious or air-gapped sites that decline external font requests.
7. Admin settings
Visit Settings → Krebf in the WordPress admin.
Theme
Amber CRT, green phosphor, paperwhite, or inherit (see the
Themes section above). Per-embed
[krebf theme="…"] still overrides this.
Games directory
Where your story files live. Empty means "use the default under
wp-content/uploads/krebf-games/". You can set a custom
absolute path, but it must sit somewhere inside wp-content/
— anything else is silently refused.
Allow external URLs
Off by default. Turning this on enables the ?game=https://…
form of URL parameter. Even when enabled, several safeguards remain active:
host allow-list (see below), SSRF IP filtering, 512 KB size cap, 10-second
timeout, Z-machine signature check.
Allowed hosts
One hostname per line. Leave empty to permit any public host (still protected by the SSRF checks). To restrict to the IF Archive, for example, enter:
ifarchive.org
*.ifarchive.org
A leading *. matches any subdomain. An empty list means
"anything not blocked by the SSRF defences is fine".
Default game
Optional. If a visitor reaches a [krebf] page with no
?game= on the URL and no game="" attribute in the
shortcode, the default game is loaded instead. Useful when you have one
"featured" title per site.
8. Playing a story
The player window divides into three bands:
- Titlebar. Logo, LED indicators (RUN / I/O / TRANS), games dropdown, and the Upload button for visitor-supplied stories.
- Display. The amber screen where the story text appears. Click inside it to restore focus to the command line.
- Controls. Save / Restore / Restart on the left, the ❯ command prompt in the middle, Transcript / About on the right.
Type commands at the ❯ prompt and press Enter. Standard IF
verbs apply: north, take lantern, read
letter, inventory, look, examine
object. In-game meta-commands save, restore,
undo, quit, verbose all work.
Special keys
| Key | Action |
|---|---|
| Enter | Submit the typed command. |
| Esc | Sends ZSCII 27 (games that handle it treat it as "cancel"). |
| ↑ ↓ ← → | In single-character input mode (map puzzles, etc.) these map to ZSCII arrow codes 129–132. |
| F1–F12 | Sent as ZSCII codes 133–144 when a story uses read_char. |
9. Save slots
Clicking Save opens a dialog prompting for a slot name. A timestamp-based default is pre-filled. Names are free-form, and re-using a name overwrites the earlier slot.
Saves live in localStorage under the key prefix
krebf_save_ — they are scoped to the browser profile and
origin, not to any WordPress user account. A private browsing window sees
an empty slot list by design.
Restore lists every save that came from the currently
loaded story file. You can delete individual slots via the ✕
icon.
Saves are local to the browser
Clearing browser data, switching devices, or using incognito mode
will leave saves inaccessible. For long plays, use the in-game
save verb regularly and consider downloading a transcript
(see below) as an out-of-band backup of your progress.
10. Transcripts
The Transcript button downloads every line of output
the game has produced in the current session as a plain .txt
file. Useful for preserving a playthrough, sharing walkthroughs, or
citing from an academic essay.
Krebf also honours the in-game transcript stream (output_stream 2
in Z-machine terms). Many Infocom games expose this as a meta-command
— type transcript or script to toggle it.
The TRANS LED lights when the stream is active.
11. External URLs
When Allow external URLs is enabled, a link of the form:
https://your-site.example/play/?game=https%3A%2F%2Fifarchive.org%2Fif-archive%2Fgames%2Fzcode%2Fminizork.z3
causes the server to:
- Resolve the hostname and verify every A/AAAA record falls within
a public IP range —
127.x.x.x,10.x,172.16–31.x,192.168.x,169.254.x,::1,fc00::/7, etc. are rejected before any HTTP is attempted. - Check the host against your allow-list (if non-empty).
- Fetch via
wp_safe_remote_getwith a 10-second timeout. - Refuse HTML responses (common phishing vector).
- Verify the first byte of the body is 1–8 (a real Z-machine version number).
- Stream the verified bytes to the browser as
application/octet-stream.
End result: the visitor loads any publicly-hosted Z-code story you allow, and your server bears the cost of the download; the browser never makes a cross-origin request of its own.
12. Auto-updates
From v1.0.9 onwards, the plugin checks
https://www.callapple.org/krebf-updates/krebf.json twice a day
for new releases. When a newer version is available it appears on the
standard Plugins screen with the same yellow notice and one-click
update button as any plugin hosted on .org.
How it works
The plugin hooks the pre_set_site_transient_update_plugins
filter and injects its own update record. The remote endpoint serves a
small JSON document with the current version, a download URL, and the
sections the “View details” lightbox renders (description,
changelog, installation). Cached for 12 hours; the cache clears
automatically when an update completes.
Forcing an immediate check
The plugin row on Plugins → Installed Plugins has a Check for updates link that wipes the cache and asks WordPress to re-poll right now. Useful after a release announcement.
Disabling auto-updates
WordPress's standard auto-update opt-in applies. On Plugins → Installed Plugins, the Krebf row has an Enable auto-updates link if you want unattended updates. Leave it untoggled for manual updates only — the plugin still tells you when one is available.
Privacy
The update poll sends a generic User-Agent
(Krebf/1.0.x (+WordPress; updater)) and nothing else
— no site URL, no admin email, no install metrics. The endpoint
returns the same JSON document to every requestor.
Air-gapped sites
If your server can't reach the public internet, the update checks will fail silently every 12 hours and time out at 10 seconds. There's no impact on the player; you'll just need to upgrade by manually uploading new releases.
13. Frequently asked questions
Why doesn't version 6 work?
V6 is Infocom's graphical branch (Journey, Shogun, Zork Zero, Arthur). It requires a window system and pixel fonts that the Ruby source this plugin was ported from never implemented. The other eight versions handle everything Infocom shipped in text mode, plus every modern Inform 6/7 output.
Will it run Blorb files?
Raw .zblorb isn't yet parsed — you'd need to extract
the Z-code chunk first. On the roadmap.
Does it work on mobile?
Yes. The virtual keyboard pops up when the command line is focused. Long sessions on a small screen are inherently awkward for parser-based games, but nothing is broken — saves, scrolling, and transcripts all behave.
Can I style it to match my theme?
Yes — three ways, in increasing order of intrusiveness:
(1) Set the plugin theme to Inherit (Settings
→ Krebf) so the player borrows your site’s colours and fonts.
(2) Use a per-embed theme attribute: [krebf
theme="paperwhite"]. (3) Override the
--krebf-* CSS variables from your theme’s stylesheet; see
the Technical Reference for the full
list.
Does it send any data anywhere?
No. No telemetry, no analytics, no phone-home. Story files come from your server; saves live in the visitor's browser; transcripts download straight to disk. The only outbound requests are optional external URL fetches the visitor explicitly triggers.
14. Troubleshooting
The dropdown is empty
The games folder is unreadable or contains no matching files. Check:
- Files are in
wp-content/uploads/krebf-games/(or your configured custom path). - Extensions are in the allowed set:
.z1–.z8,.dat,.story. - File sizes are under 512 KB.
- The web server has read permission on the folder.
"Story not found" on ?game=slug
Most often a filename mismatch. zork1.z3 matches
?game=zork1 but not ?game=Zork1 (filenames are
case-sensitive on Linux hosts). Verify the actual filename on disk.
"This URL is not permitted"
External URLs disabled, the host isn't on your allow-list, or the target resolves to a private IP. Enable the feature in Settings, add the host, and confirm it's publicly addressable.
Story loads, but garbled output
Most likely a truly exotic Z-code variant or a file corrupted in transit. Try loading the file in another interpreter (Frotz, Bocfel, Gargoyle) to confirm; if those also fail, the file itself is bad.
Conflict with another plugin
All Krebf styles are scoped under .krebf-root and all
JavaScript is inside an IIFE, so conflicts should be rare. If you spot
one, it's most likely another plugin's stylesheet using aggressive
* { } or button { } rules that win on specificity.
Bump Krebf's specificity with a theme override:
.krebf-root .krebf-btn {
/* your overrides */
}
See the Technical Reference for architectural detail, REST endpoints, hook catalogue, and the interpreter's internal structure.