The World Vol 1 · A World That Ticks
ch 01 / 105
Chapter 01

The Workstation

Verifying the workstation

The first machine in The World is the workstation: the compiler, the container engine, and the directory where the server starts. Never trust an installed tool until it has run something and you have read the output.

An installer finishing without complaint proves the installer ran. It does not prove the compiler compiles or the container engine starts containers. Each tool you install today earns its place by doing its actual job once, in front of you, before anything depends on it.

The world is the reason for the strictness. It will run on a machine you control: ground with real soil chemistry, water that flows where the terrain says it must, plants that compete for light, creatures that eat the plants and each other, and a village of characters who remember what happened to them.

The client program is also yours. It lets you walk in, stand in that village, and be remembered. The world ticks while you sleep. It keeps its own history. It never resets.

None of that exists yet, and the book does not hand it over whole. You build the server that owns all of it, the simulation that advances it, the wire protocol that carries it, and the renderer that shows it.

The language is Go. The runtime home is a container. By the last page of this volume, the first server lives in a box that can keep counting time whether or not you are logged in.

Create the theworld module and its worldd command below. Run the formatter, vet and tests before adding simulation behavior.

Go and the first program

⚙ Tool — installing Go 1.26

Download the archive for your operating system from go.dev/dl and follow the three-line install on that page (on Linux: unpack to /usr/local, add /usr/local/go/bin to your PATH, open a new terminal). This volume targets Go 1.26; the reference output below records Go 1.26.3 on Linux/amd64.

A newer compatible compiler can build the examples, but version strings and diagnostics can differ. Exact transcript replay needs the reference toolchain, not merely a compatible language version. Go's install page is the reference: go.dev/doc/install.

▣ Build · stage 1 — make the compiler introduce itself
go version
$ go version
go version go1.26.3 linux/amd64

Your line will differ in the last digit and probably in the tail. The patch number climbs over time. linux/amd64 names this machine's operating system and processor, so a Mac prints darwin/arm64 and Windows prints windows/amd64.

Use Go 1.26 or a compatible newer release for learning. Keep 1.26.3 when checking this quoted version line.

If the command is not found at all, the PATH step above did not take. Open a fresh terminal before debugging anything else, because the old one still has the old PATH.

The version line proves the toolchain exists. Now make it do its job. Create a scratch directory anywhere (it will not be part of the project; ~/scratch is fine) and put one file in it.

▣ Build · stage 2 — the first program
// hello.go
package main

import "fmt"

func main() {
	fmt.Println("the world is not running yet")
}
$ go run hello.go
the world is not running yet

Seven lines, and five of them carry the program. package main declares that this file belongs to a program instead of a library. Go programs start in a package with exactly that name.

import "fmt" pulls in the standard library's formatting package, the one that knows how to print. func main() is the entry point: when the program starts, this function runs, and when it returns, the program ends.

go run did two jobs in one command: it compiled the file into a real machine-code binary in a temporary location, then executed it. There is no interpreter here. Every Go program in this book is compiled first, every time, and the compile is fast enough that you rarely see it happening.

◆ Note — the editor

Any text editor works. If you want one with Go support wired in, VS Code plus its Go extension gives you error underlines and formatting on save, free.

The book never depends on editor features. Everything is checked from the command line, so what your editor thinks is never the authority on whether the code is right.

The module and worldd

A scratch file proves the compiler works, but a project of this size needs a structure the tools recognize. Go's unit of project is the module: a directory tree with a go.mod file at its root.

That file names the module and records which Go version and which outside libraries it depends on. Everything you build across all twenty-five volumes lives in one module. Create its home and initialize it:

▣ Build · stage 3 — initialize the theworld module
mkdir theworld
cd theworld
go mod init theworld
go mod edit -go=1.26
$ go mod init theworld
go: creating new go.mod: module theworld
go: to add module requirements and sums:
	go mod tidy
$ cat go.mod
module theworld

go 1.26

The second suggested command, go mod tidy, matters later, when the module gains dependencies. Today there are none to tidy.

The go mod edit line does one small thing, and it matters. go mod init stamps the file with the exact toolchain that ran it, 1.26.3 on this machine and some other patch on yours.

The go directive declares a minimum Go version and selects the language version. It can include a patch-level minimum, but it never locks the compiler to that exact patch.

Setting it to go 1.26 permits Go 1.26 or newer, and it means your go.mod now matches the one every later chapter quotes, byte for byte. The file is two facts: this module is named theworld, and its code is Go 1.26.

The name carries real weight. It becomes the root of every import path in the project, and that matters as soon as packages begin to refer to one another.

Inside the module, layout is convention. Go projects put each runnable program in its own directory under cmd/, named after the binary it produces.

This one is cmd/worldd: the world daemon, the server that will own every rock, river, creature, and villager. The trailing d is an old Unix habit marking a program built to run continuously in the background. By the end of this volume it has work to justify the letter.

The theworld module: what each path means to the go command A diagram of the module directory tree. The root directory theworld contains go.mod and a cmd directory; cmd contains worldd, which contains main.go. Arrows annotate that go.mod names the module, that cmd/worldd is the package path used in go run ./cmd/worldd, and that main.go holds func main, the entry point. theworld/ go.mod names the module; marks its root cmd/worldd/ the path in: go run ./cmd/worldd main.go package main + func main = entry point
Figure 1.1 — The whole skeleton. go.mod tells the toolchain where the module starts and what it is called; ./cmd/worldd is how you name the package you want run; main.go is where execution begins. Every command in this book that starts with go is run from the directory holding go.mod.
▣ Build · stage 4 — worldd draws its first breath
// cmd/worldd/main.go
package main

import (
	"fmt"
	"runtime"
)

const version = "0.0.1"

func main() {
	fmt.Println("worldd", version, "starting")
	fmt.Println("go runtime:", runtime.Version())
	fmt.Println("nothing exists yet: no ground, no water, no time")
}
$ go vet ./...
$ go run ./cmd/worldd
worldd 0.0.1 starting
go runtime: go1.26.3
nothing exists yet: no ground, no water, no time

Two new pieces of Go appeared. The import block now lists two packages in parentheses, one per line. const version = "0.0.1" declares a named constant: a value fixed at compile time, so nothing can change it while the program runs.

The second output line comes from runtime.Version(), the standard library reporting which Go built the running binary. Your line shows your own patch release.

The command that printed nothing matters too. go vet ./... checks every package in the module (./... means "here and everything below") for mistakes the compiler technically permits. Silence is a pass. Run it before every run in this book.

⚠ Worked failure — the import that refused to ride along

While writing stage 4 you might reasonably think ahead: the server will surely need os, the package for talking to the operating system, so import it now and save a trip. Add "os" to the import block without using it, and:

$ go run ./cmd/worldd
# theworld/cmd/worldd
cmd/worldd/main.go:5:2: "os" imported and not used

The program did not run. This is a compile error, not a warning: Go refuses to build a file that imports a package and never touches it.

Read the message the way you will read hundreds like it. The # line names the package that failed to compile, and the next line gives file, line 5, column 2: the exact position of the offending import.

The reasoning from symptom to cause is one step here. The policy behind it is the lesson: in a codebase that will grow for twenty-five volumes, an import is a claim that a dependency is real, and Go keeps every claim honest by force. Delete the line (or never add it) and the build comes back. Import when you use, not when you predict.

Podman containers

Podman runs the world's supporting services and reference checks in containers. Each container has a separate user-space view, but shares the host kernel on Linux or a helper VM's kernel on Windows and macOS.

Rootless Podman avoids a privileged daemon. Processor architecture still matters for replay.

⚙ Tool — installing podman

On most Linux distributions it is one package: sudo apt install podman, sudo dnf install podman, or your distribution's equivalent. On macOS and Windows, install Podman Desktop and run podman machine init then podman machine start once, which creates the small Linux virtual machine containers need there. Full instructions: podman.io/docs/installation.

▣ Build · stage 5 — a container earns its keep
podman --version
podman run --rm docker.io/library/alpine:3.22 echo "a container said this"
$ podman --version
podman version 5.7.0
$ podman run --rm docker.io/library/alpine:3.22 echo "a container said this"
a container said this

On the first run, Podman may print image-download progress. run starts the container. --rm removes it after exit. The arguments after the image name select its command.

A later run can reuse the cached image. That speedup is useful, but the cached image is also one more reason exact replay has to record what image was used.

Replay conditions

A replay claim names its conditions: the same source and inputs, deterministic draw and update order, an exact compiler and dependencies, and a reference platform. A seed alone does not fix all of those.

Later simulation code uses floating-point arithmetic. Its final bits can depend on the architecture and compiler even when every operation appears in a fixed order. The book's byte comparisons test the recorded reference environment; they do not prove identical results on any machine.

Today's go.mod records a compatibility requirement. Likewise, alpine:3.22 and golang:1.26 are mutable tags: a publisher can move either to new content while your local cache still holds the old image.

Exact replay requires an image reference ending in @sha256:… and an explicit platform such as --platform linux/amd64, plus the recorded Go version. Inspect a pulled image with podman image inspect and record its RepoDigests and platform before using the digest reference.

The early volume scripts retain tags. They do not record the original image digest, so resolving a tag today cannot recover that missing provenance. The contract compatibility note records this limit and the digest-pinned references the later volumes actually use.

Checkpoint

✓ Checkpoint — what you can now do
  • Install a Go toolchain and prove it works with two commands: one that prints its version and one that compiles and runs a real program.
  • Explain what package main, import, and func main() each do in the seven-line program from this chapter.
  • Create a Go module, read its go.mod, and say why the module name is the root of every import path to come.
  • Run go vet ./... and interpret both of its answers: silence, and a file-line-column report.
  • Start a container from a version-tagged image, explain each part of the podman run command, and say where the image came from and why the second run was faster.
  • Explain why a tag is weaker than a digest.
  • Handed the error "os" imported and not used, name the policy behind it and fix it in one line.
⚡ Exercises — try first, then reveal
Exercise 1 — a second binary. The module can hold a second program: worldc, the client path. Create cmd/worldc/main.go with a main that prints worldc: no world to connect to yet, then run it. What did you have to change compared to stage 4, and what does go vet ./... now cover?

Almost nothing changes: same package main, same entry point, different directory, and go run ./cmd/worldc picks it by path. Both programs can be package main because packages are scoped by directory, one package per directory. go vet ./... now checks both binaries in one command; the ./... pattern grows with the module for free, so the book standardizes on it.

Exercise 2 — break it on purpose, twice. From your home directory (not the module), run go run ./cmd/worldd and read the error. Then, back inside the module, change func main() to func Main() and run it again. Which of Go's rules did each error enforce?

The first prints go: go.mod file not found in current directory or any parent directory: package paths like ./cmd/worldd are resolved relative to a module, and Go searches upward from where you stand until it finds a go.mod. The second fails because the entry point must be exactly main, lowercase; Main is just an ordinary function nobody calls, so the linker reports that main is undeclared. Both errors name their rule in the message; Go usually does.

Exercise 3 — Go inside a container. Without installing anything new, run podman run --rm docker.io/library/golang:1.26 go version. Predict the output before you run it, including how it can differ from your own go version.

After a pull (this image is far larger than Alpine; give it a minute) it prints a go version go1.26.x linux/… line. The patch number can differ from your host's because the image carries its own toolchain, and a later pull of the same tag can update it. The platform reads linux even on a Mac or Windows machine, because the container runs Linux; the architecture follows the selected image platform. Record both before comparing reference output.