Take the AmiSSL/OpenSSL ABI emulation that a314bsd now bakes in (its
amissl.library offloads TLS over the A314 bus to a Raspberry Pi) and generalise
it: offload TLS to a generic machine on the LAN (an Ubuntu box, anything that
runs Python) reached over ordinary TCP sockets, instead of being tied to the
A314 bus and a Pi.
The win: the AmiSSL shim no longer assumes the A314 ecosystem. It rides whatever
bsdsocket.library is resident — Roadshow, AmiTCP, Miami, or a314bsd — so any
Amiga with a working TCP stack can do HTTPS in iBrowse/AWeb while the slow crypto
runs on a fast LAN host.
iBrowse / AWeb
│ OpenSSL / AmiSSL LVO calls
▼
amissl.library + amisslmaster.library ← AmiSSL/OpenSSL ABI shim (ABI-frozen)
│ "open a connection, relay these bytes"
▼
[transport module] ──TCP via resident bsdsocket──► LAN TLS daemon ──TLS──► server
(Roadshow / AmiTCP / Miami / a314bsd) (Ubuntu, Python)
aminet.net loaded over HTTPS on a real Amiga 500 (68EC030, Roadshow, BlueSCSI DaynaPORT WiFi), TLS offloaded to a LAN daemon — 2026-08-23, the first fully-working real-hardware run.
Working end-to-end on real 68k hardware (v2.0, 2026-08-23): verified on an
Amiga 500 (68EC030, Roadshow, WiFi DaynaPORT), an Amiga 2000 (X-Surf) and a CD32
(SLIP) — Amelinium and iBrowse-class browsers load https://aminet.net and
other modern-TLS sites through the offload, full pages with all inline images.
The chain is
browser → amisslmaster → amissl shim → tls_proxy.py → TLS to server → plaintext relay.
(v1.0 was validated only in WinUAE — which, it turned out, masked two 68k ABI bugs that made it fail on every real machine; see the v2.0 release notes for that story. Emulator validation of hand-rolled library calls is not validation.)
How it works: at SSL_connect the shim opens a socket to the daemon and sends
CONNECT <host> <port>; once the daemon has completed the verified TLS handshake to
the real server it Dup2Sockets that socket onto the app's fd and relays plaintext, so
SSL_read/SSL_write become plain recv/send. The port is read from the app's own
connected fd (getpeername), so the offload is generic — HTTPS, IMAPS, etc. The daemon
is daemon/tls_proxy.py.
Getting AWeb and clean concurrent image loading working took three non-obvious fixes; see Lessons learned below.
Operational notes: iBrowse caps concurrent connections (~5), so the daemon closes
each connection as soon as the response body is delivered, or later images starve
(handled in tls_proxy.py).
Two halves: a daemon on a fast LAN machine (Linux, Raspberry Pi, Mac, PC) and the
shim (amissl.library + amisslmaster.library) on the Amiga. Prebuilt libraries
and both installers are in dist/.
Requires Python 3 only (standard library; no pip packages).
Linux / Raspberry Pi (systemd — installs and starts on boot):
cd dist
sudo ./install-daemon.sh # listen on 0.0.0.0:8443
# sudo AMISSL_PORT=9000 ./install-daemon.sh # custom portThis copies tls_proxy.py to /opt/amissl-tunnel/, creates a sandboxed systemd
service amissl-tunnel, starts it, and prints the IP + port to enter on the
Amiga. Manage it with:
systemctl status amissl-tunnel
journalctl -u amissl-tunnel -f # live request logmacOS / Windows / WSL, or just to try it — run it directly:
python3 dist/tls_proxy.py 0.0.0.0 8443Firewall: allow inbound TCP on the chosen port (default 8443) from your LAN. The daemon makes the outbound TLS connection and holds the system CA bundle, so it does the certificate verification; the Amiga↔daemon hop is plaintext on your trusted LAN.
Requires a working TCP/IP stack that provides bsdsocket.library — Roadshow,
AmiTCP, Miami, or a314bsd.
Copy the contents of dist/ to the Amiga (network share, ADF, etc.), then from a
Shell inside that folder:
Installer Install
The installer backs up any existing LIBS:amissl.library / amisslmaster.library
to *.bak (so you can restore real AmiSSL later), copies the two shim libraries to
LIBS:, and asks for the daemon address + port, saving it to ENV: and
ENVARC:AMISSLPROXY.
Manual install (no Installer):
Copy amissl.library LIBS:
Copy amisslmaster.library LIBS:
SetEnv SAVE AMISSLPROXY 192.168.1.50:8443 ; your daemon's IP:port
(SetEnv SAVE writes both ENV: and ENVARC:.)
With the daemon running and your stack up, launch iBrowse, AWeb, or Amelinium and
open an https:// site such as https://aminet.net. The daemon log should show
CONNECT aminet.net:443 → … → HTTP/1.1 200.
ENV:AMISSLPROXY = host:port
host— the daemon's LAN IP (e.g.192.168.1.50) or a hostname your stack resolves.port— the daemon's port (default8443).- Unset/unparseable → the shim falls back to its compiled default
127.0.0.1:8443(handy for local/VM testing).
It's read once at the first TLS connect. The outbound port the daemon dials is taken
from the app's own socket (getpeername), so HTTPS, mail (IMAPS / POP3S / SMTPS),
IRC, and FTPS all work — not just port 443.
Restore the backups (Copy LIBS:amissl.library.bak LIBS:amissl.library and the
master), or just delete the two shim libraries if you had no prior AmiSSL.
cd amiga && make under WSL with the bebbo amiga-gcc toolchain on PATH builds both
libraries — see amiga/Makefile.
These are the non-obvious things that cost time; they are baked into the current amiga/amissl.c.
-
Sentinel pointers must look like real pointers. AWeb range-checks every
SSL/SSL_CTXit receives from AmiSSL against[0x1000, 0xFFFFFFF0)and frees+rejects anything below0x1000(see AWeb'sGetSharedSSLCTX). The shim originally returned1forSSL_CTXand a small session index (1..16) forSSL, so AWeb aborted the secure connection right afterSSL_CTX_new. iBrowse never range-checked, so1worked there. Fix:SSL_CTX → 0x4000,SSL handle → 0x5000 + index(decoded back insess_from_ssl). -
The diagnostic probe build does not survive concurrency — ship with
AMISSL_PROBE 0. With probes on, every AmiSSL LVO opens its own TCP connection to the daemon to emit a marker. Fine sequentially; under a burst of parallel image fetches it floods/races and the real connections fail. Probes are a debug aid only. -
A bsdsocket
SocketBaseis per-task; the shim must not keep it in one shared field.InitAmiSSLAreceives the app'sSocketBaseper task, but the shim stored it in the single sharedbase->BsdBase. Browsers fetch inline images in ~6–7 parallel tasks; each init clobbered the shared field, so a task'ssess_connectran socket I/O on another task's base — the daemon saw a connect with noCONNECTline ("0 bytes read") and the image failed. The timeline proof was unmistakable: simultaneous bursts all failed, the browser's one-at-a-time retries succeeded. Fix: ag_task_bb[]map (task → SocketBase) filled byInitAmiSSLA/ cleared byCleanupAmiSSLA;SSL_newpins the caller task's base intostruct SslSess.bbase;sess_connect/sess_read/sess_writeuses->bbase, never the shared field. This is what took both browsers from "partial, flaky" to "14/14 images, zero failures, first pass."The shim lineage this grew from is now folded into a314bsd (which bakes in its own AmiSSL shim). a314bsd offloads over the A314 bus rather than a resident LAN
bsdsocket.library, so its concurrency characteristics differ — but per-taskSocketBaseisolation is the general lesson for any shared-base AmiSSL shim. -
Browser-side image behaviour ≠ transport. AWeb's default
IMAGELOADINGis notALL, so it instantiates inline<img>as suppressed placeholders and never fetches them (a plain refresh serves cache). Force with ARexxIMAGELOADING ALL+RELOAD IMAGES(portAWEB.#), and make it permanent withSAVESETTINGS(writesENVARC:AWeb3/). iBrowse autoloads images by default. -
Write Amiga scripts LF-only. A Windows-CRLF
S/autotestmakes AmigaDOS pass the trailing\ras part of a URL argument →GET /path\r HTTP/1.1→400 Bad Request. Use WSLprintf, not PowerShellSet-Content.
- AWeb source (
Source/AWebAPL/amissl.c, ARexx docs) — © 2002 Yvon Rozijn, changes © 2025 amigazen project, distributed under the AWeb Public License. Reading AWeb's open source is what revealed the[0x1000, 0xFFFFFFF0)pointer range-check (lesson 1) and theIMAGELOADING/RELOAD IMAGES/SAVESETTINGSARexx commands (lesson 4). We did not copy AWeb code — we studied it to understand what AWeb expects from AmiSSL. - AmiSSL / OpenSSL ABI — the LVO table and ABI emulated here follow AmiSSL (OpenSSL on AmigaOS).
- a314bsd — the parent project this forks the idea from; it now bakes in its own AmiSSL shim (offloading TLS over the A314 bus to a Pi). See "Relationship to existing projects" below.
- Test site: aminet.net — a small, Amiga-friendly HTTPS site used as the rendering target.
- Tooling: WinUAE (
bsdsocket_emu), the amiga-gcc/Bebbo cross toolchain, and Python/asyncio for thetls_proxy.pydaemon.
| Path | Contents |
|---|---|
amiga/ |
The shim: amissl.c/amisslmaster.c (+ _start.S LVO tables). make under WSL (amiga-gcc) builds amissl.library (~53 KB) + amisslmaster.library. |
daemon/tls_proxy.py |
The LAN daemon — this is what the 68k shim speaks to. Run it: python3 tls_proxy.py 0.0.0.0 8443 |
daemon/smoke_test.py |
Smoke test for the daemon |
dist/ |
Ready-to-install package: Install script, prebuilt libraries, daemon + install-daemon.sh. |
docs/DESIGN.md |
Architecture and design notes |
- a314bsd — replaces
bsdsocket.librarywith a proxy that forwards BSD socket calls over A314 to a Raspberry Pi, and now bakes in its own AmiSSL shim (amissl.library+amisslmaster.library) that offloads TLS over the A314 bus to the Pi. AmiSSL-Tunnel takes the same idea but offloads to a generic LAN host over ordinary TCP, so it runs on any residentbsdsocket.library(Roadshow / AmiTCP / Miami — including a314bsd) instead of requiring A314 hardware. - a314SSLlib — the original dev sandbox where the AmiSSL ABI shim was first debugged. No longer a separate project: that work was folded into a314bsd.
