Skip to content

Repository files navigation

GobanFTP

English | 简体中文 | 日本語

A record of the board game Go where the filename is the event.

Perl 5.34+ Version 1.100_001 License Apache-2.0 Showcase test

Current beta release: v1.1.0-beta.1/package 1.100_001.

Filename is the event · The fork · Why this exists · See it first · What this is for · Not for · Three-minute check · Terminal play · Static specimen · The contract

The Filename Is the Event

A small game can be only names:

g1.id-replay.s3.r-chinese-area-v1.k0.pb-alice.pw-bob/
  events/
    m1.p000001.b.play-aa.pa-genesis.by-alice.n-chain1.h-khjclcui7pejbv3m
    m1.p000002.w.play-bb.pa-khjclcui7pejbv3m.by-bob.n-chain2.h-bihb3re4k9hlucat
    m1.p000003.b.pass.pa-bihb3re4k9hlucat.by-alice.n-chain3.h-kcvtlonfje163p9q

There is no move body to read. The filename is the event.

The game directory basename names the board, rules, komi, and players. The direct child basenames under events/ name the accepted events. Replay follows the parent ids in those names, not file contents, mtimes, or listing order. Other files may exist, but only accepted event basenames participate in replay.

The Fork

If two publish attempts create different legal children of the same parent, the listing cannot pick a winner:

g1.id-replay.s3.r-chinese-area-v1.k0.pb-alice.pw-bob/
  events/
    m1.p000001.b.play-aa.pa-genesis.by-alice.n-forkleft.h-q65v2mhef9t3em7l
    m1.p000001.b.play-bb.pa-genesis.by-alice.n-forkright.h-o5g8u5cu913nedng

Both events claim pa-genesis. Default replay reports a visible fork instead of letting FTP, WebDAV, Git, DNS, filesystem listing order, mtime, file bodies, or sidecar metadata silently decide the game.

Why This Exists

GobanFTP is a GOFTP/1 protocol experiment with a runnable proof specimen. It checks one narrow claim: if the same game descriptor basename and accepted event basenames are visible, the same Go game can be replayed.

It started as playful protocol abuse / protocol bending: making FTP-shaped storage do something outside its usual job. The point is not to build a normal online Go game server; it is to make a replay boundary visible on untrusted enumerable storage.

File contents, size, mtime, listing order, sidecars, projections, SGF, HTML, terminal output, and source art can help humans inspect the game, but they do not decide it.

It is not a normal online Go game server, a hosted Web UI, or a production security system.

See It First

GobanFTP static witness specimen showing a visual 9x9 board and witness fields.

After replay, the same accepted names can be projected into a board and witness page.

Open examples/static/witness-specimen.html directly in a browser.

It has no script, no server, and no network fetch. It only displays witness fields and a board projection generated from replay. The page is not the game source; replay still comes from the game descriptor and event filenames.

What This Is For

GobanFTP is a good candidate if you want to explore:

  • deterministic replay from public descriptor and event filenames
  • event logs shaped as directory listings
  • visible fork diagnostics when writers race
  • protocol boundaries over untrusted enumerable storage
  • protocol art that still runs

Not For

GobanFTP is not:

  • a normal online Go game server
  • a hosted Web UI
  • a production auth system
  • a production FTP safety proof
  • DNS resolver or provider integrations
  • a complete scoring/result system

Three-Minute Check

Requirements: Perl 5.34+ and make. No FTP server is required for this local check; it uses fixtures in the repository.

perl Makefile.PL
make
make test
prove -lr t/showcase-demo.t t/showcase-v1_1.t
perl -Ilib script/gobanftp showcase --out showcase-v1.1

The showcase test checks this boundary: a clean game replays, a race becomes a visible fork, and display surfaces, file bodies, and metadata do not silently decide the game.

gobanftp showcase --out showcase-v1.1 writes a local direct-open static bundle from checked-in fixtures. It is display output for local inspection, not a hosted Web UI and not replay input.

Those views are inspection output: static HTML is not hosted Web UI, and --surface terminal is not the local play --tui input surface.

Run the clean fixture directly:

GOBANFTP_ROOT=examples/fixtures/ftp-shrine \
perl -Ilib script/gobanftp play --once g1.id-ftp-shrine.s9.r-chinese-area-v1.k7500.pb-daemon.pw-pilgrim

Selected output:

gobanftp.play=ok
events=7
canonical_moves=6
worldline.status=main
  a b c d e f g h i
9 . . . . . . . . .
8 . . . . . . . . .
7 . . . . . . . . .
6 . . . B . . . . .
5 . . . B . W . . .
4 . . . B W W . . .
3 . . . . . . . . .
2 . . . . . . . . .
1 . . . . . . . . .

Now open the race fixture:

GOBANFTP_ROOT=examples/fixtures/ftp-race-shrine \
perl -Ilib script/gobanftp replay g1.id-ftp-race-shrine.s9.r-chinese-area-v1.k7500.pb-daemon.pw-pilgrim

Exit code 3 is expected for this fixture. It means the race was kept visible:

diagnostic ... code=fork parent_id=hihat4p8r6gaeuts
gobanftp.replay=fork
events=4
event_set_count=4
canonical_moves=1
legal_moves=3
canonical_ids=hihat4p8r6gaeuts

Five Terms

  • event filename: a move or acknowledgement named under events/.
  • replay: rebuild the game from accepted names.
  • fork: a visible race where two valid children claim the same parent.
  • projection: generated display, such as board text, SGF, or HTML.
  • witness: proof material for humans or tests; not the game truth itself.

First Look

These are views of the same object. The static witness specimen is shown above. The replay input remains the game directory basename plus the direct event filenames under events/.

Protocol Object

GobanFTP protocol object: game descriptor directory, event basenames, sidecar, projections, and tmp residue.

The game is visible as a tree. events/ contains the accepted names. sidecar/, projections/, and tmp/ may explain, display, or help publish, but they do not decide replay.

Race Becomes Fork

GobanFTP race fixture replay output showing a visible fork diagnostic.

If two moves extend the same parent, listing order does not choose one. Default replay reports the fork and stops unless explicit ack-assisted recovery is requested.

Terminal Input Surface

GobanFTP terminal play surface with keyboard and optional SGR mouse two-step confirmation.

play --tui is local input and display over the same replay and publish callbacks. Keyboard and SGR mouse, where available, select a candidate first. A second Enter or click confirms it. Input is locked while publishing.

Terminal Play

Try local terminal play with a disposable copy:

tmp="$(mktemp -d)"
src="$(find examples/fixtures/ftp-shrine -maxdepth 1 -type d -name 'g1.id-ftp-shrine*' | head -n 1)"
cp -R -- "$src" "$tmp/"
game="$tmp/$(basename "$src")"
perl -Ilib script/gobanftp play --tui "$game"

The publish path is deliberately two-step:

select -> confirm -> publishing_locked -> published

Arrow keys or hjkl (Vim-style) move the cursor. Enter selects a point; pressing Enter again on the selected point confirms publish. SGR mouse clicks use the same select/confirm flow where the terminal supports them. One successful publish ends the session.

The TUI does not own rules, roots, diagnostics, or event acceptance. It is only an input/display layer over replay and publish callbacks.

For read-only live-over-listing observation, use bounded watch --live or play --live:

perl -Ilib script/gobanftp watch --live --max-polls 3 --interval 1 "$game"
perl -Ilib script/gobanftp watch --live --compact --max-polls 3 --interval 1 "$game"

Live mode keeps polling after visible forks or validation diagnostics. It does not choose a winner, and it does not publish moves. It only keeps re-listing events/, replaying the names, and showing the current witness surface. --compact keeps the event-set and worldline fields while omitting the board.

Static Witness Specimen

examples/static/witness-specimen.html is a direct-open specimen. It has no script, no network fetch, no server process, and no hosted UI behavior.

The visual board is a projection view beside raw projection text. It can display fields that were already generated; it cannot make an event valid.

The Contract

GOFTP/1 has two replay inputs:

Truth Meaning
game descriptor directory basename names the game, rules, and players
direct child basenames under events/ names moves and acknowledgements

Replay ignores everything else:

Shadow Examples
file data entry type, bytes, size
server metadata mtime, listing order, server order
FTP commands RETR, SIZE, MDTM
WebDAV metadata ETag, Last-Modified, locks, resource bodies
display and helper paths sidecar/**, projections/**, tmp/**
generated surfaces SGF, static HTML, terminal output, source art

The game can be replayed after deleting every projection. Change file contents, mtime, or listing order and replay stays the same. Change an event filename and the game must change, or the event must be rejected.

Event ids are derived from canonical filename context, not from file contents. All known play forms a DAG. A network race becomes a visible fork; it is not hidden by FTP or WebDAV ordering.

Protocol names use a small public alphabet:

[a-z0-9._-]

Do not put secrets in filenames.

Fixture Layout

The browsable fixture is a protocol object, not a screenshot:

g1.id-ftp-shrine.s9.r-chinese-area-v1.k7500.pb-daemon.pw-pilgrim/
  events/
    m1.p000001.b.play-dd.pa-genesis.by-daemon.n-altar1.h-0agr68rv1sp5qi21
    ...
  sidecar/
  projections/
    board/current.txt
    oracle/listing.txt
    sgf/main.sgf
  tmp/

Read the tree like this:

g1.../         names the game
events/       names the moves and acknowledgements
sidecar/      may explain, but cannot decide
projections/  may display, but cannot decide
tmp/          is publishing residue

Useful first files:

examples/fixtures/ftp-shrine/README.md
examples/fixtures/ftp-shrine/g1.id-ftp-shrine.s9.r-chinese-area-v1.k7500.pb-daemon.pw-pilgrim/events/
examples/fixtures/ftp-shrine/g1.id-ftp-shrine.s9.r-chinese-area-v1.k7500.pb-daemon.pw-pilgrim/projections/oracle/listing.txt
examples/fixtures/ftp-shrine/g1.id-ftp-shrine.s9.r-chinese-area-v1.k7500.pb-daemon.pw-pilgrim/projections/board/current.txt
examples/fixtures/ftp-shrine/g1.id-ftp-shrine.s9.r-chinese-area-v1.k7500.pb-daemon.pw-pilgrim/projections/sgf/main.sgf
oracle/goban.pl

projections/oracle/listing.txt is a reader-facing transcript. It demonstrates that NLST events/ exposes event basenames while RETR, SIZE, and MDTM remain outside GOFTP/1 replay.

SGF is generated from replay. It is not read back as the source game.

What Runs Now

Implemented in v1.1.0-beta.1/package 1.100_001:

  • Consensus core: filename grammar, event ids, event_set_root, DAG replay, chinese-area-v1 rules, SGF, and ack-assisted fork recovery.
  • Stores: local, FTP, WebDAV, read-only Git tree, and read-only DNS record-file admission.
  • Interfaces: play --tui, witness text/html/terminal output, projections, direct-open static specimen, read-only watch --live / play --live observers, and executable source-art smoke.
  • Profiles: unsigned GOFTP/1, declared substrate profiles, and explicit signed-HMAC witness/preflight checks.
  • Checks and fixtures: showcase test, attack fixtures, cross-substrate golden vectors, and profile publish fixtures.

Deliberately out of scope in v1.1.0-beta.1/package 1.100_001:

  • git-tree-goftp1 is read-only at runtime; publish commands fail at the storage boundary.
  • dns-record-goftp1 is read-only normalization of a local or otherwise declared record file. DNS admission does not query live DNS, run AXFR, trust DNSSEC, call provider APIs, or publish records.
  • TTL, answer order, cache age, DNSSEC status, authoritative server identity, and provider metadata stay outside consensus.
  • Live DNS is not implemented by the DNS record-file profile.
  • Static HTML witness output is not hosted Web UI, and --surface terminal is not the local play --tui input surface.
  • Verifier-local HMAC key files, explicit verifier-supplied lifecycle status, and fixture publish-token/preflight semantics are not production key lifecycle, production auth, or real writer authorization.
  • Production auth and production key lifecycle are not implemented in this release.
  • Final scoring/result events remain outside GOFTP/1.

FTP listing-shadow public poison-vector coverage is fixture/listing evidence only. It does not claim RETR, SIZE, MDTM, live FTP auth, live FTP integrity, or production FTP deployment safety. The ftp-goftp1 tmp+rename publish path is declared separately and covered by mock FTP tests. Live provider smoke remains outside the current beta source-gate/review scope.

Signed/auth material in this release is verifier-local fixture/preflight coverage. It is not production writer authorization or production key lifecycle.

Unsigned GOFTP/1 remains valid and unchanged. A signed/auth profile can reject events only when that explicit profile is selected; sidecar signatures do not alter unsigned replay.

Source Art Boundary

oracle/goban.pl may look like a Go board. It still has to run:

perl -c oracle/goban.pl
perl oracle/goban.pl --smoke

Expected output includes:

oracle/goban.pl syntax OK
gobanftp.oracle=ok
rules.move=ok

The source art can dispatch to tested modules. It does not define filename grammar, event ids, DAG replay, rule legality, storage behavior, SGF, or diagnostics. Whitespace, comments, POD, C hooks, and asm-like text are presentation, not replay input.

Run It

Runtime requirements:

Perl 5.34+
Digest::SHA
HTTP::Tiny
MIME::Base64
Net::FTP

Build and test requirements:

make

Optional:

Inline
Inline::C

Normal test run:

perl Makefile.PL
make
make test

Full local prove run:

prove -lr t

Create a disposable game:

tmp="$(mktemp -d)"
export GOBANFTP_ROOT="$tmp"

perl -Ilib script/gobanftp create-game --id demo --size 9 --black alice --white bob
perl -Ilib script/gobanftp publish-move g1.id-demo.s9.r-chinese-area-v1.k7500.pb-alice.pw-bob aa
perl -Ilib script/gobanftp publish-move g1.id-demo.s9.r-chinese-area-v1.k7500.pb-alice.pw-bob play-bb
perl -Ilib script/gobanftp play --once g1.id-demo.s9.r-chinese-area-v1.k7500.pb-alice.pw-bob

Inspect the accepted packets:

find "$GOBANFTP_ROOT" -path '*/events/*' -exec basename {} \; | sort

Those names are the game input. File contents are not.

Stores

Local is the default store. FTP, read-only Git tree, read-only DNS record-file admission, and WebDAV use the same listing-first boundary without reading event file contents, blob bytes, resource bodies, or DNS transport metadata. When a local argument is a path, only the final path component is used as the game descriptor basename, and that basename must be a valid GOFTP game descriptor.

FTP mode:

GOBANFTP_STORE=ftp
GOBANFTP_FTP_HOST
GOBANFTP_FTP_USER
GOBANFTP_FTP_PASSWORD
GOBANFTP_FTP_ROOT
GOBANFTP_FTP_PORT
GOBANFTP_FTP_PASSIVE
GOBANFTP_FTP_TIMEOUT
GOBANFTP_FTP_PUBLISH_MODE

WebDAV mode:

GOBANFTP_STORE=webdav
GOBANFTP_WEBDAV_URL
GOBANFTP_WEBDAV_USER
GOBANFTP_WEBDAV_PASSWORD
GOBANFTP_WEBDAV_TOKEN
GOBANFTP_WEBDAV_TIMEOUT
GOBANFTP_WEBDAV_CLASS
GOBANFTP_WEBDAV_PUBLISH_MODE

Authenticated WebDAV URLs must use https://; Basic and Bearer credentials are rejected on http://. Unauthenticated http:// remains available for mock/local cleartext fixtures and is not a production transport-safety mode.

Git tree mode:

GOBANFTP_STORE=git-tree
GOBANFTP_GIT_REPO
GOBANFTP_GIT_TREEISH
GOBANFTP_GIT_BINARY

DNS record mode:

GOBANFTP_STORE=dns-record
GOBANFTP_DNS_RECORD_FILE
GOBANFTP_DNS_OWNER_SUFFIX

Git tree replay reads direct child names from <treeish>:<game>/events and ignores blob bytes, commit metadata, refs, branches, tags, sidecars, projections, and tmp entries. Git tree mode is read-only for now; publish commands fail at the storage boundary.

DNS record admission reads only a local or otherwise declared record-file presentation for dns-record-goftp1, supplied at runtime by GOBANFTP_DNS_RECORD_FILE. It is not a live DNS resolver, AXFR client, DNSSEC validator, provider API client, dynamic update client, or publishing backend. TTLs, record order, answer order, cache age, DNSSEC status, authoritative server identity, and provider metadata are ignored before event_set_root.

WebDAV replay reads events/ with PROPFIND Depth: 1 and uses only direct href basenames. Publishing writes a zero-byte temporary resource under tmp/, moves it to events/<event-name>, then confirms visibility with a fresh PROPFIND.

For ftp-goftp1, default publishing uploads a zero-byte temporary entry under tmp/, renames it to events/<event-name> with RNTO, and confirms visibility by listing. GOBANFTP_FTP_PUBLISH_MODE=mkdir remains the directory-shaped alternative. This path does not claim live FTP auth, live FTP integrity, or production FTP deployment safety.

Projection writes are local-only for now. Nonlocal project and sgf --write are rejected; plain sgf, verify, replay, play, and watch can read nonlocal listings.

Release Checks

Main commands:

prove -lr t/showcase-demo.t
prove -lr t

The current beta source gate is in docs/V1_1_RELEASE_GATE.md. It records the fixture-local checks for v1.1.0-beta.1/package 1.100_001 and explicitly omits tag, push, upload, deploy, and distribution commands.

Public beta release notes are in docs/V1_1_RELEASE_NOTES.md. Historical v1.0/P14 release records remain in docs/P14_RELEASE_GATE.md and docs/P14_RELEASE_MANIFEST_AND_TAG_PLAN.md; they are not the current beta release/tag identity.

For the current beta source-gate/review scope, live provider smoke, distribution packaging, tag, upload, and deploy are outside the beta gate and require a later separate maintainer-run gate.

License

Unless otherwise noted, the code, protocol documentation, examples, fixtures, test vectors, projections, and static specimens in this repository are licensed under the Apache License, Version 2.0.

Copyright 2026 GobanFTP contributors.

This license covers the repository contents. It does not grant permission to access, test, or publish to third-party FTP, WebDAV, DNS, Git, or other systems without authorization, and it is not a production security certification.

Release Invariants

GobanFTP is not a game server. It is a Perl implementation of a small filename protocol for replaying Go from accepted event basenames across several enumerable stores.

The release checks compare event names, event_set_root, DAG replay, canonical prefix, board projection, SGF, and the diagnostic class for the same observable logical failure. The expected behavior is:

modify mtime       -> unchanged
modify file bytes  -> unchanged
modify LIST order  -> unchanged
add sidecar        -> unchanged
change basename    -> changed
bad signed profile -> rejected by that signed profile
source art / C / asm / Web UI / TUI -> cannot change truth

v0.1 froze the GOFTP/1 consensus boundary. The original v1.0/P14 package 1.000 release source applied that boundary across local files, FTP, WebDAV, read-only Git tree replay, read-only DNS record-file admission, terminal play, static witness output, and source art smoke. The current beta release line is v1.1.0-beta.1/package 1.100_001.

Documentation

Quick links:

Showcase:     docs/SHOWCASE.md
Protocol:     docs/PROTOCOL.md
Profiles:     docs/PROFILES.md
Grammar:      docs/GRAMMAR.md
Attacks:      docs/ATTACKS.md
v1.1 gate:    docs/V1_1_RELEASE_GATE.md
v1.1 notes:   docs/V1_1_RELEASE_NOTES.md
v1.0 DoD:     docs/V1_DOD.md
v1.0 history: docs/P14_RELEASE_GATE.md
Algorithms:   docs/ALGORITHMS.md
Rules:        docs/RULES.md
Diagnostics:  docs/DIAGNOSTICS.md
Source art:   docs/SOURCE_ART.md
Build:        docs/BUILD.md
CLI:          docs/CLI.md
Roadmap:      docs/ROADMAP.md
Decisions:    docs/DECISIONS.md

Repository map:

.
|-- README.md              this text
|-- README.zh-CN.md        Simplified Chinese README
|-- README.ja.md           Japanese README
|-- docs/                  protocol, roadmap, decisions, release records
|-- oracle/goban.pl        executable source-art smoke wrapper
|-- lib/GobanFTP/          Perl implementation modules
|-- script/gobanftp        CLI entry point
|-- examples/fixtures/     browsable mirrored games
`-- t/                     tests and attack fixtures

Before changing protocol behavior, read:

  1. docs/PROTOCOL.md
  2. docs/ARCHITECTURE.md
  3. docs/ALGORITHMS.md
  4. docs/RULES.md
  5. docs/ROADMAP.md
  6. docs/DECISIONS.md

Start with the existing protocol docs before adding a new profile or rule.

About

A Go game can live in an FTP listing.

Resources

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages