summaryrefslogtreecommitdiff
path: root/README.md
blob: 70de28ddea401e8807e8eddce4801c44f427e523 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# 🐧 penguins.lan

A little intranet for a battery-powered LAN with no internet. The successor to
`inetravel` — same "have fun on the network" spirit, but a real Go app instead
of PHP includes, and actually fun stuff: live chat now, an r/place-style shared
canvas next.

## Stack

- **Go** standard-library HTTP server, no framework.
- **SQLite** (`mattn/go-sqlite3`) for persistence.
- **gorilla/websocket** for the realtime hub.
- `html/template` for server-rendered pages.

## Architecture

Requests flow through a small continuation-passing pipeline. Each route in
[`api/serve.go`](api/serve.go) reads as a flat chain of `Continuation`s, where
every link gets a `(success, failure)` pair and decides which to call. Shared
state for a request lives on `types.RequestContext`.

```
LogRequest -> ResolveSession -> RequireUser -> <feature continuations> -> Template -> LogTime
```

Layout:

| path             | what                                                        |
|------------------|-------------------------------------------------------------|
| `main.go`        | entrypoint: load env, open db, migrate, serve               |
| `args/`          | flag parsing                                                |
| `database/`      | sqlite conn, migrations, per-feature queries                |
| `adapters/arp/`  | resolves a client IP -> MAC from the host's ARP table       |
| `api/`           | server wiring (`serve.go`) + feature packages               |
| `api/auth/`      | **MAC-based identity** — sessions, portal, reclaim          |
| `api/ws/`        | **websocket hub** — broadcast chat, foundation for realtime |
| `api/template/`  | html/template rendering + 404 handling                      |
| `templates/`     | server-rendered pages, wrapped in `base.html`               |
| `static/`        | css / js / images                                           |

## Identity (`api/auth` + `adapters/arp`)

No passwords — on a trust-based LAN your **MAC address is your credential**. The
router (nodogsplash) keeps every client on one L2 segment, so the server reads
each visitor's MAC from the ARP table (`arp -an`).

- **First visit** → the portal asks for a handle; claiming it creates a `user`,
  binds your current MAC, and sets a session cookie.
- **Return visit** → a live session cookie logs you in; if it's gone, a
  recognised MAC logs you straight back in.
- **Rotated MAC** (privacy randomization) → you show up as a new device. The
  portal reports when that handle was *last online* and lets you **log back in**
  to reclaim it, re-binding your new MAC to the existing account.

One user can own many MACs (`macs` table). Chat messages reference `user_id`
(not a name), so identity survives MAC rotation and renames.

> **Local dev:** the browser is on loopback and never appears in the ARP table,
> so set `DEV_MAC=aa:bb:cc:dd:ee:ff` to force a MAC and exercise the whole flow.
> Never set it in production — it would make every visitor one shared identity.

## Realtime (`api/ws`)

The `Hub` owns the set of connected clients and a broadcast channel; all client
set mutations happen in its single `Run()` goroutine, so no locks. Each `Client`
has a `readPump` and `writePump`. Messages are JSON tagged by `type`
(`chat` / `system` / `history`). To add a new realtime feature (e.g. the canvas),
add a `type` and handle it in the hub + client — no new connection plumbing.

## Run it

```sh
go mod download
go run . --server --migrate
# -> http://localhost:8080
```

Flags: `--server`, `--migrate`, `--port 8080`,
`--database-path ./db/penguins.db`, `--template-path ./templates`,
`--static-path ./static`.

### Docker

```sh
docker compose up --build
```

## Roadmap

- [x] base framework + live chat over websockets
- [x] MAC-based accounts + portal with reclaim
- [ ] r/place-style shared canvas
- [ ] presence / who's online
- [ ] more LAN games