ClassicStack is an AppleTalk router and classic LAN services stack that bridges legacy Macintosh and DOS networking into modern environments. Always in beta.
- AppleTalk Phase 2 routing across EtherTalk and LocalTalk transports.
- AFP file server over both classic DDP and modern TCP transports.
- MacIP gateway for IP-over-AppleTalk clients.
- MacIPX gateway for IPX-over-AppleTalk clients.
- Optional IPX, NetBEUI, NetBIOS, and SMB1 services (build-tag gated).
- Shared raw-link bridge settings for EtherTalk, MacIP, IPX, and NetBEUI.
Grab the latest release from Github Releases releases.
Doom running over MacIPX over AppleTalk over LtOUDP through Snow, back to IPX on 86box.
Requirements:
- Go 1.23+
- Npcap on Windows for pcap mode: https://npcap.com/#download
- libpcap on Linux/macOS for pcap mode
Build default binary (all optional protocol hooks enabled):
go build -tags all -o classicstack ./cmd/classicstackBuild with a custom protocol tag set:
go build -tags "ipx netbeui netbios smb" -o classicstack ./cmd/classicstackor:
go build -tags all -o classicstack ./cmd/classicstackBuild router-only variant (no optional build-tag services):
go build -o classicstack ./cmd/classicstackRun tests:
go test ./...- Copy server.toml.example to server.toml.
- Edit bridge/device/network values.
- Run with no flags (auto-loads server.toml) or pass -config.
Examples:
./classicstack -config server.toml.\classicstack.exe -config server.tomlConfig loading rules:
- -config cannot be combined with other flags.
- When no flags are passed, server.toml is loaded automatically if present.
Bridge defaults live in [Bridge] and are reused by EtherTalk, MacIP, IPX, and NetBEUI.
| Key | Type | Default | Description |
|---|---|---|---|
| mode | string | pcap | Raw-link backend: pcap, tap, tun. |
| device | string | (empty) | Interface/device name used by shared raw-link consumers. |
| hw_address | string | DE:AD:BE:EF:CA:FE | Shared host MAC identity. |
| bridge_mode | string | auto | Frame adaptation mode: auto, ethernet, wifi. |
Important: legacy bridge keys under [EtherTalk] are no longer accepted in config files. Use [Bridge] only.
Per-protocol pcap filter overrides:
- [EtherTalk].filter
- [MacIP].filter
- [IPX].filter
- [NetBEUI].filter
These filters apply only in pcap mode.
Declares which transports the AppleTalk router binds to. An enabled transport that is not bound runs standalone: it still comes up and receives frames (and can be captured), but it is not part of the AppleTalk router — no RTMP/ZIP and no inter-port forwarding. This lets you run, say, TashTalk on its own segment without it joining the router.
| Key | Default | Notes |
|---|---|---|
| ports | (empty) | Transport section names the router binds to ("LToUdp", "TashTalk", "EtherTalk"). Empty (or section omitted) binds every enabled transport; a non-empty list binds only those named, so any enabled-but-unlisted transport runs standalone. |
[Router]
ports = ["LToUdp", "EtherTalk"] # TashTalk, if enabled, runs standaloneThe dashboard shows each port's routed: on/off so you can see at a glance
which transports are part of the router. The same allow-list is editable from
the web UI via the "Attach to AppleTalk router" checkbox on each transport.
| Key | Default | Notes |
|---|---|---|
| enabled | true | Enables LocalTalk-over-UDP port. |
| interface | 0.0.0.0 | Local IPv4 bind/join interface. |
| seed_network | 1 | Seed network ID for this segment. |
| seed_zone | LToUDP Network | Seed zone name. |
| Key | Default | Notes |
|---|---|---|
| port | (empty) | Serial device path/name; empty disables. |
| seed_network | 2 | Seed network ID for this segment. |
| seed_zone | TashTalk Network | Seed zone name. |
| Key | Default | Notes |
|---|---|---|
| bridge_host_mac | (empty) | Optional host adapter MAC for wifi bridge shim. |
| filter | (protocol default) | Optional BPF override in pcap mode. |
| seed_network_min | 3 | Seed network range start. |
| seed_network_max | 5 | Seed network range end. |
| seed_zone | EtherTalk Network | Seed zone name. |
| Key | Default | Notes |
|---|---|---|
| enabled | false | Enables MacIP gateway. |
| mode | pcap | pcap or nat. |
| zone | (empty) | Registration zone override. |
| nat_subnet | 192.168.100.0/24 | Subnet/pool for NAT mode. |
| nat_gw | (empty) | Gateway address advertised in NAT mode. |
| lease_file | (empty) | Optional lease persistence file. |
| ip_gateway | (empty) | Upstream gateway address. |
| dhcp_relay | false | Translate/relay DHCP for clients. |
| nameserver | (empty) | DNS server for clients. |
| filter | (protocol default) | Optional BPF override in pcap mode. |
IPX is optional and requires build tag ipx or all.
| Key | Default | Notes |
|---|---|---|
| enabled | false | Enables IPX router services. |
| interface | (empty) | Raw-link interface; empty reuses bridge device. |
| framing | ethernet_ii | One of ethernet_ii, raw_802_3, llc, snap. |
| internal_network | (empty) | 8 hex digits; empty falls back to default network. |
| filter | ipx (internal default) | Optional BPF override in pcap mode. |
NetBEUI is optional and requires build tag netbeui or all.
| Key | Default | Notes |
|---|---|---|
| enabled | false | Enables NetBEUI raw-link port. |
| interface | (empty) | Raw-link interface; empty reuses bridge device. |
| filter | llc (internal default) | Optional BPF override in pcap mode. |
NetBIOS is optional and requires build tag netbios or all.
| Key | Default | Notes |
|---|---|---|
| enabled | false | Enables NetBIOS service. |
| transports | ["tcp"] | Allowed values: tcp, netbeui, ipx. |
| scope_id | (empty) | Optional NetBIOS scope ID. |
NetBIOS server/workgroup identity is derived from SMB server/workgroup values.
SMB is optional and requires build tag smb or all.
| Key | Default | Notes |
|---|---|---|
| enabled | false | Enables SMB server. |
| nbt_binding | :139 | NetBIOS-over-TCP listener. |
| direct_binding | (empty) | Optional direct SMB listener (for example :445). |
| guest_ok | false | Allows guest sessions. |
| server_name | CLASSICSTACK | Computer/server name. |
| workgroup | WORKGROUP | Workgroup/domain label. |
SMB shares are configured as [SMB.Volumes.] sections.
Example:
[SMB]
enabled = true
nbt_binding = ":139"
guest_ok = true
server_name = "CLASSICSTACK"
workgroup = "WORKGROUP"
[SMB.Volumes.Public]
name = "Public"
path = "./public"
fs_type = "local_fs"
read_only = falseAFP runs over ddp, tcp, or both.
| Key | Default | Notes |
|---|---|---|
| enabled | true | Enables AFP service. |
| name | ClassicStack (example) | Advertised AFP server name. |
| zone | (empty) | Registration zone override. |
| protocols | ddp,tcp | AFP transports. |
| binding | :548 | DSI listener. |
| extension_map | (empty) | Extension map file path. |
| cnid_backend | sqlite | sqlite or memory. |
| use_decomposed_names | true | Reserved-character mapping behavior. |
| appledouble_mode | modern | modern or legacy sidecar layout. |
AFP volumes are configured as [AFP.Volumes.] sections.
[Logging]:
- level: debug, info, warn
- parse_packets: protocol decode logging
- parse_output: file target for parsed logs
- log_traffic: raw traffic logging
[Capture]:
- localtalk, ethertalk, ipx capture output paths
- snaplen for capture truncation length
A management web UI is available in builds that include -tags webui (which
-tags all does). It serves a dashboard showing per-service status, bindings,
and live (SSE-streamed) statistics, plus a configuration editor, read-only
diagnostics (zone/network enumeration), and a live log viewer.
[WebUI]:
- enabled: turn the listener on (default off)
- bind:
IP:PORTto listen on (default127.0.0.1:8080, loopback) - tls: serve HTTPS (default true); a self-signed certificate is generated at
startup when
cert_pem/key_pemare blank - cert_pem / key_pem: paths to a PEM certificate and key (supply both, or leave both blank for the self-signed certificate)
Equivalent flags: -webui-enabled, -webui-bind, -webui-tls,
-webui-cert-pem, -webui-key-pem.
From the dashboard you can start, stop, and restart the standalone services
(IPX, NetBEUI, NetBIOS, SMB) live; stops are dependency-aware (stopping NetBIOS
also stops SMB). The configuration editor can edit scalar settings, add /
update / remove AFP volumes and SMB shares, and toggle packet-dump and pcap
capture options (parse-packets, traffic logging, and per-transport capture
file paths). The Logs tab streams the server's log output live (recent
history is replayed on open, then new lines append) with a client-side level
filter. Edits stage in memory; Apply re-wires the running stack (the web
UI server is preserved across the rebuild), Save writes server.toml
(backing up the prior file to server.toml.NNNN and dropping comments), and
Download backup exports the current config. The same operations are exposed
by the transport-agnostic pkg/control API, so a future text/telnet UI can
reuse them.
ClassicStack ships a wrapper binary so it can run in the background and start
automatically. It shares the same runtime as classicstack, so the config and
behaviour are identical — it just manages the process lifecycle. The wrapper is a
different command per platform:
Run from an elevated (Administrator) prompt:
# Register the service (auto-start at boot) pointing at a config file:
.\classicstack-svc.exe install -config C:\ProgramData\ClassicStack\server.toml
.\classicstack-svc.exe start # start it now
.\classicstack-svc.exe status # query the state
.\classicstack-svc.exe stop # stop it
.\classicstack-svc.exe uninstall # remove itThe service is named ClassicStack (visible in services.msc and
Get-Service ClassicStack) and writes start/stop entries to the Application event
log. classicstack-svc.exe run -config ... runs the stack in the current console
for debugging.
classicstackd self-daemonizes — it needs no systemd or other init system:
# Start detached in the background (writes a PID file and logs to a file):
classicstackd start -config /etc/classicstack/server.toml \
-pidfile /var/run/classicstack.pid -log /var/log/classicstack.log
classicstackd status # report whether it is running
classicstackd stop # stop it gracefully (SIGTERM)
classicstackd run -config /etc/classicstack/server.toml # foreground (Ctrl-C to stop)-pidfile and -log default to /var/run/classicstack.pid and
/var/log/classicstack.log. For boot persistence, point your init system's
ExecStart at classicstackd run -config <path>.
On macOS, install/uninstall additionally manage a LaunchAgent so the daemon
runs as a login item (headless):
classicstackd install -config ~/Library/Application\ Support/ClassicStack/server.toml
# writes ~/Library/LaunchAgents/com.obsoletemadness.classicstack.plist and loads it
classicstackd uninstall # unload + remove the LaunchAgentList pcap devices:
.\classicstack.exe -list-pcap-devicesPrint version:
./classicstack -versionWarning: this project is pragmatic and evolving. Validate behavior in your environment before production use.
AppleTalk routing was originally inspired by tashrouter: https://github.com/lampmerchant/tashrouter
GPL-3.0.
- High-level runtime map: ARCHITECTURE.md
- Protocol notes: spec
