Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/pr-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,13 @@ jobs:
include:
- os: ubuntu-latest
script: bash scripts/ci/build.sh
output: out/omnitalk
output: out/classicstack
- os: macos-latest
script: bash scripts/ci/build.sh
output: out/omnitalk
output: out/classicstack
- os: windows-latest
script: ./scripts/ci/build.ps1
output: out/omnitalk.exe
output: out/classicstack.exe
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -132,7 +132,7 @@ jobs:
sudo apt-get update
sudo apt-get install -y libpcap-dev

- name: Build omnitalk (Linux/macOS)
- name: Build classicstack (Linux/macOS)
if: runner.os != 'Windows'
shell: bash
env:
Expand All @@ -141,7 +141,7 @@ jobs:
OUTPUT: ${{ matrix.output }}
run: ${{ matrix.script }}

- name: Build omnitalk (Windows)
- name: Build classicstack (Windows)
if: runner.os == 'Windows'
shell: pwsh
env:
Expand Down
36 changes: 18 additions & 18 deletions .github/workflows/release-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,57 +49,57 @@ jobs:
# Linux - all
- os: ubuntu-latest
variant: all
artifact_name: omnitalk-linux
archive_name: omnitalk-${{ needs.version.outputs.release_tag }}-linux-amd64.tar.gz
artifact_name: classicstack-linux
archive_name: classicstack-${{ needs.version.outputs.release_tag }}-linux-amd64.tar.gz
build_script: bash scripts/ci/build.sh
package_script: bash scripts/ci/package-release.sh
target_os: linux
output: out/omnitalk
output: out/classicstack
# Linux - router
- os: ubuntu-latest
variant: router
artifact_name: omnitalk-router-linux
archive_name: omnitalk-router-${{ needs.version.outputs.release_tag }}-linux-amd64.tar.gz
artifact_name: classicstack-router-linux
archive_name: classicstack-router-${{ needs.version.outputs.release_tag }}-linux-amd64.tar.gz
build_script: bash scripts/ci/build.sh
package_script: bash scripts/ci/package-release.sh
target_os: linux
output: out/omnitalk-router
output: out/classicstack-router
# macOS - all
- os: macos-latest
variant: all
artifact_name: omnitalk-macos
archive_name: omnitalk-${{ needs.version.outputs.release_tag }}-macos-amd64.zip
artifact_name: classicstack-macos
archive_name: classicstack-${{ needs.version.outputs.release_tag }}-macos-amd64.zip
build_script: bash scripts/ci/build.sh
package_script: bash scripts/ci/package-release.sh
target_os: macos
output: out/omnitalk
output: out/classicstack
# macOS - router
- os: macos-latest
variant: router
artifact_name: omnitalk-router-macos
archive_name: omnitalk-router-${{ needs.version.outputs.release_tag }}-macos-amd64.zip
artifact_name: classicstack-router-macos
archive_name: classicstack-router-${{ needs.version.outputs.release_tag }}-macos-amd64.zip
build_script: bash scripts/ci/build.sh
package_script: bash scripts/ci/package-release.sh
target_os: macos
output: out/omnitalk-router
output: out/classicstack-router
# Windows - all
- os: windows-latest
variant: all
artifact_name: omnitalk-windows
archive_name: omnitalk-${{ needs.version.outputs.release_tag }}-windows-amd64.zip
artifact_name: classicstack-windows
archive_name: classicstack-${{ needs.version.outputs.release_tag }}-windows-amd64.zip
build_script: ./scripts/ci/build.ps1
package_script: ./scripts/ci/package-release.ps1
target_os: windows
output: out/omnitalk.exe
output: out/classicstack.exe
# Windows - router
- os: windows-latest
variant: router
artifact_name: omnitalk-router-windows
archive_name: omnitalk-router-${{ needs.version.outputs.release_tag }}-windows-amd64.zip
artifact_name: classicstack-router-windows
archive_name: classicstack-router-${{ needs.version.outputs.release_tag }}-windows-amd64.zip
build_script: ./scripts/ci/build.ps1
package_script: ./scripts/ci/package-release.ps1
target_os: windows
output: out/omnitalk-router.exe
output: out/classicstack-router.exe
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ go.work.sum
/leases.txt

# Generated by scripts/ci/build.ps1
/cmd/omnitalk/resource.syso
/cmd/omnitalk/versioninfo.json
/cmd/classicstack/resource.syso
/cmd/classicstack/versioninfo.json

._htmlcache/
.macgarden/
30 changes: 15 additions & 15 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# OmniTalk Architecture
# ClassicStack Architecture

OmniTalk is a Go AppleTalk Phase 2 router and AFP file server. It bridges
ClassicStack is a Go AppleTalk Phase 2 router and AFP file server. It bridges
legacy Apple networking protocols to modern environments — EtherTalk
(raw Ethernet), LToUDP (multicast UDP), TashTalk (serial), and
virtual LocalTalk transports — and serves AFP volumes over both the
Expand All @@ -13,7 +13,7 @@ import.
## Module map

```
cmd/omnitalk/ wiring only — flag/INI parsing, service registration
cmd/classicstack/ wiring only — flag/INI parsing, service registration
config/ single typed config tree; INI loader, validation
protocol/ wire format only (codec + constants, zero I/O)
ddp/ DDP datagram + MacRoman codec
Expand Down Expand Up @@ -59,11 +59,11 @@ cmd → service → (protocol | port | pkg)
not about higher protocols.
- `service/*` owns sockets, sessions, and state machines. It composes
`protocol` codecs over `port` transports.
- `pkg/*` is reusable outside OmniTalk. It must not import anything
- `pkg/*` is reusable outside ClassicStack. It must not import anything
under `service/`, `port/`, `cmd/`, or `router/`.
- `internal/*` is private to OmniTalk. Mocks and shared test harness
- `internal/*` is private to ClassicStack. Mocks and shared test harness
live here.
- `cmd/omnitalk/` does no business logic. It parses configuration
- `cmd/classicstack/` does no business logic. It parses configuration
and wires services together.

## Core interfaces
Expand All @@ -86,7 +86,7 @@ Single typed tree in `config/`. Two loaders feed it:

1. TOML — `config.Load(path)` parses `server.toml` via `knadh/koanf`
with the `pelletier/go-toml` v2 parser.
2. Flags — `cmd/omnitalk/main.go` overlays CLI flags on top of the
2. Flags — `cmd/classicstack/main.go` overlays CLI flags on top of the
file defaults.

`config.Root.Validate()` runs once before services start. Services
Expand All @@ -95,14 +95,14 @@ are immutable: ports do not mutate themselves after `Start()`.

## Logging and telemetry

OmniTalk has two logging packages with distinct jobs:
ClassicStack has two logging packages with distinct jobs:

- **`netlog/`** is the call-site API. Services and ports use
`netlog.Debug`, `netlog.Info`, `netlog.Warn`. The facade keeps call
sites short (no per-package `*slog.Logger` plumbing) while still
routing through whatever structured handler `cmd/omnitalk` installs.
routing through whatever structured handler `cmd/classicstack` installs.
- **`pkg/logging/`** is the slog factory used once at startup.
`cmd/omnitalk` calls `logging.New("OmniTalk", ...)` to build a
`cmd/classicstack` calls `logging.New("ClassicStack", ...)` to build a
`*slog.Logger` with the configured handler (console, JSON, or both)
and installs it via `netlog.SetLogger`. Use this directly only when
you need a `*slog.Logger` value — e.g. attaching structured fields
Expand All @@ -114,14 +114,14 @@ format, and the slog handler stamps every record with a `source`
attribute that JSON consumers can filter on.

Stdlib `log.Printf` and `log.Fatal` are not used inside library code.
`cmd/omnitalk/main.go` uses `log.Fatal*` only for unrecoverable startup
`cmd/classicstack/main.go` uses `log.Fatal*` only for unrecoverable startup
errors before any logger is wired.

Telemetry is `pkg/telemetry`, separate from logs. Default backend is
`expvar` (stdlib, zero deps). Initial counters:
- `omnitalk_router_frames_in_total`
- `omnitalk_afp_commands_total`
- `omnitalk_aarp_probe_retries_total`
- `classicstack_router_frames_in_total`
- `classicstack_afp_commands_total`
- `classicstack_aarp_probe_retries_total`

A future `//go:build otel` file will swap in an OpenTelemetry backend
without touching call sites.
Expand Down Expand Up @@ -150,7 +150,7 @@ proceeds one type per commit with golden hex round-trip tests.

## Timer and retry patterns

OmniTalk does not use exponential backoff. The protocols predate it.
ClassicStack does not use exponential backoff. The protocols predate it.
Three canonical shapes:

1. **Reliable-delivery retransmits** (ATP-style). Per-transaction
Expand Down
14 changes: 7 additions & 7 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project Overview

OmniTalk is a Go-based AppleTalk Phase 2 router and AFP file server. It bridges legacy Apple networking protocols to modern environments, supporting EtherTalk (raw Ethernet), LToUDP (multicast UDP), TashTalk (serial), and virtual LocalTalk transports.
ClassicStack is a Go-based AppleTalk Phase 2 router and AFP file server. It bridges legacy Apple networking protocols to modern environments, supporting EtherTalk (raw Ethernet), LToUDP (multicast UDP), TashTalk (serial), and virtual LocalTalk transports.

**Module:** `github.com/pgodw/omnitalk`
**Module:** `github.com/ObsoleteMadness/ClassicStack`
**Go version:** 1.23.0

## Commands

```bash
# Build
go build -o omnitalk ./cmd/omnitalk
go build -o classicstack ./cmd/classicstack

# Run all tests
go test ./...
Expand All @@ -22,21 +22,21 @@ go test ./...
go test ./service/afp/...

# Run with TOML config
./omnitalk # auto-loads server.toml if present
./classicstack # auto-loads server.toml if present

# Run with flags (see README.md for full list)
./omnitalk -ethertalk eth0 -zone "MyZone"
./classicstack -ethertalk eth0 -zone "MyZone"
```

## Architecture

### Core Data Flow

```
cmd/omnitalk/main.go → Ports → Router → Services
cmd/classicstack/main.go → Ports → Router → Services
```

1. **Entry point** (`cmd/omnitalk/`) parses CLI flags and `server.toml`, constructs ports, wires them to the router, and starts services.
1. **Entry point** (`cmd/classicstack/`) parses CLI flags and `server.toml`, constructs ports, wires them to the router, and starts services.
2. **Router** (`router/`) receives DDP datagrams from all ports, maintains the `RoutingTable` and `ZoneInformationTable`, and dispatches to services by socket number or forwards to other ports.
3. **Ports** (`port/`) abstract network interfaces. All implement `port.Port` (Unicast/Broadcast/Multicast). Implementations: `ethertalk`, `localtalk/ltoudp`, `localtalk/tashtalk`, `localtalk/virtual`.
4. **Services** (`service/`) plug into the router by registering socket numbers. Each implements `service.Service`.
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ TAGS ?= all
.PHONY: build test test-race test-tags lint vuln gosec fuzz clean

build:
go build -tags "$(TAGS)" -o omnitalk ./cmd/omnitalk
go build -tags "$(TAGS)" -o classicstack ./cmd/classicstack

test:
go test -tags "$(TAGS)" ./...
Expand All @@ -30,5 +30,5 @@ fuzz:
done

clean:
rm -f omnitalk omnitalk.exe
rm -f classicstack classicstack.exe
rm -rf out dist
Loading
Loading