Files
galaxy-game/ui/core/README.md
T
Ilia Denisov a89048f6c5 docs(ui): finalize MVP plan structure and de-archaeologize topic docs
MVP web client (Phases 1-30) is complete; reorganize planning + living docs around that.

- PLAN.md kept as the staged MVP record (1-30) with a status block + pointers; removed the 31-36 stages, regression scenarios, and deferred-TODO section (moved out); fixed a stale cross-machine plan path.

- ui/PLAN-finalize.md (new): active web-finalization plan in 8 stages (visual system, a11y, i18n, error UX, PWA, build hygiene, docs, owner manual-QA loop); absorbs former Phases 33 and 35.

- ui/ROADMAP.md (new): post-MVP (Wails, Capacitor, realistic projection, acceptance + regression scenarios) and triaged deferred follow-ups.

- ui/docs/README.md (new): grouped topic-doc index.

- De-archaeologized all 20 ui/docs topic docs + ui/README.md + ui/core/README.md: stripped Phase-N build history, rewritten as current-state; deferred work now points at ROADMAP.md / PLAN-finalize.md. Docs-only; no code change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 23:17:51 +02:00

167 lines
7.3 KiB
Markdown

# ui/core — Galaxy Client Compute Module
`ui/core` (Go module `galaxy/core`) is the compute boundary of the
Galaxy cross-platform UI client. It carries v1 transport-envelope
canonical bytes, signature verification, and Ed25519 keypair
helpers. Network I/O and persistent storage live elsewhere on
purpose: this module compiles unchanged to WASM (the web target today)
and is designed to also compile to gomobile (mobile) and Wails-embedded
native (desktop) — both planned, see [`../ROADMAP.md`](../ROADMAP.md).
The authoritative byte contract is defined in
[`docs/ARCHITECTURE.md` §15](../../docs/ARCHITECTURE.md). The gateway
mirrors this exact wire format in its own
[`gateway/authn`](../../gateway/authn) package; cross-module byte
parity and round-trip sign/verify are exercised by
[`gateway/authn/parity_with_ui_core_test.go`](../../gateway/authn/parity_with_ui_core_test.go).
## Invariants
- **No network.** No `net/http`, no `net/url`, no gRPC client.
- **No storage.** No `os` (outside `_test.go` fixtures), no SQL, no
filesystem, no keychain.
- **TinyGo-friendly.** Production files do not import
`crypto/x509`, `encoding/pem`, or any package not supported by
the WASM target. PKCS#8 PEM is server-only and stays in
`gateway/authn`.
- **No goroutines, no channels, no `sync` primitives** in
production files. Pure functions, deterministic outputs.
- **No re-export of `crypto/ed25519` types** in the public API.
Callers see opaque `[]byte` blobs and `string` representations
so the WASM bridge can hand them across the JS boundary as
`Uint8Array` and string primitives.
- **Randomness is injected.** `keypair.Generate(reader io.Reader)`
takes a caller-supplied reader. Production code passes
`crypto/rand.Reader`; tests use deterministic `bytes.NewReader`;
WASM later passes a `crypto.getRandomValues` adapter.
## Layout
```text
ui/core/
├── go.mod module galaxy/core (Go 1.26.0)
├── calc/ thin wrappers over `pkg/calc/` (no math here)
│ ├── ship.go ship geometry, mass, speed, combat
│ ├── planet.go ship build cost / per-turn production
│ ├── solve.go goal-seek inverse solvers
│ └── number.go display rounding (Ceil3)
├── canon/ canonical-bytes builders and verifiers
│ ├── canon.go length-prefix helpers
│ ├── request.go galaxy-request-v1 fields and signing input
│ ├── response.go galaxy-response-v1 fields and verifier
│ ├── event.go galaxy-event-v1 fields and verifier
│ ├── signature.go base64 client-key request verification
│ └── testdata/ committed JSON golden vectors
├── keypair/ Ed25519 generate / sign / verify / marshal
└── types/ full transport envelopes + result codes
```
## Public API
### `galaxy/core/canon`
- `RequestDomainMarkerV1`, `ResponseDomainMarkerV1`, `EventDomainMarkerV1`
— UTF-8 domain prefixes that bind a signature to a specific
envelope kind.
- `RequestSigningFields`, `ResponseSigningFields`, `EventSigningFields`
— exact subsets of envelope fields covered by the v1 signature.
- `BuildRequestSigningInput`, `BuildResponseSigningInput`,
`BuildEventSigningInput` — produce canonical bytes ready for
`ed25519.Sign` / `ed25519.Verify`.
- `VerifyRequestSignature(clientPublicKey string, signature []byte,
fields RequestSigningFields) error` — accepts the base64
string form the backend stores in the device session.
- `VerifyResponseSignature(publicKey ed25519.PublicKey, signature []byte,
fields ResponseSigningFields) error`,
`VerifyEventSignature(publicKey ed25519.PublicKey, signature []byte,
fields EventSigningFields) error` — used by the client to
validate server output.
- `VerifyPayloadHash(payloadBytes, payloadHash []byte) error`.
- Sentinel errors: `ErrInvalidPayloadHash`, `ErrPayloadHashMismatch`,
`ErrInvalidClientPublicKey`, `ErrInvalidRequestSignature`,
`ErrInvalidResponseSignature`, `ErrInvalidEventSignature`.
### `galaxy/core/keypair`
- `Generate(reader io.Reader) (privateKey, publicKey []byte, err error)`.
- `Sign(privateKey, message []byte) ([]byte, error)` — returns a 64-byte
raw Ed25519 signature.
- `Verify(publicKey, message, signature []byte) bool`.
- `MarshalPublicKey(publicKey []byte) (string, error)` — base64
StdEncoding, the wire format documented in §15.
- `UnmarshalPublicKey(value string) ([]byte, error)`.
- `PublicKeyFromPrivate(privateKey []byte) ([]byte, error)`.
- Sentinel errors: `ErrInvalidPrivateKey`, `ErrInvalidPublicKey`,
`ErrInvalidPublicKeyEncoding`.
### `galaxy/core/calc`
Thin Go bridges over `pkg/calc/`, surfaced via WASM to the ship-class
calculator and the ship-group inspector. Each function is a one-line
passthrough — no math lives here. Coverage spans ship geometry / mass /
speed / cargo, combat (attack, defence, bombing), block-upgrade and
per-turn ship-build cost, the single-target goal-seek inverse solvers,
and display rounding (`Ceil3`).
The authoritative function surface (which UI feature uses what, parity
rules, what is still deferred) lives in
[`ui/docs/calc-bridge.md`](../docs/calc-bridge.md).
### `galaxy/core/types`
- `RequestEnvelope`, `ResponseEnvelope`, `EventEnvelope` — full Go
envelope structs mirroring the protobuf messages in
`gateway/proto/galaxy/gateway/v1/`. Each exposes a
`SigningFields()` method to project onto the corresponding
`canon.*SigningFields`.
- `ProtocolVersionV1 = "v1"`, `ResultCodeOK = "ok"` — the only
result string that is part of the stable client contract; any
other `result_code` is downstream-opaque and must not be
hard-coded by clients.
## Testing
```sh
go test -count=1 ./ui/core/...
```
The `canon` test suite combines:
- byte-equality on golden JSON fixtures under
`canon/testdata/` for three request types
(`user.account.get`, `lobby.my.games.list`,
`user.games.command`), one response (`ok`), and one event
(`gateway.server_time`);
- mutation tests proving every signed field is bound into the
signature;
- round-trip sign-then-verify across all three envelope kinds;
- negative tests for tampered hashes, mismatched timestamps and
request IDs, invalid signature lengths, and bad public-key
encodings.
Cross-module parity (gateway accepts ui/core signatures and vice
versa) is enforced from
`gateway/authn/parity_with_ui_core_test.go`.
## What this module is **not**
- Not a network client. ConnectRPC over `@connectrpc/connect-web`
on the TypeScript side is the only network surface.
- Not a key store. Per-platform secure storage lives in the platform
`KeyStore` / `Cache` layer on the TypeScript side.
- Not a freshness gate. Server-side `±5 min` freshness checks
remain in `gateway/internal/grpcapi/freshness_replay.go`. The
client is expected to stamp its own `timestamp_ms` accurately
via `time.Now`, but does not enforce a window.
- Not a FlatBuffers codec — report decoding lives on the TypeScript
side, so this module stays small on purpose.
## Cross-references
- [`../../docs/ARCHITECTURE.md` §15](../../docs/ARCHITECTURE.md) —
authoritative byte contract.
- [`../../gateway/authn`](../../gateway/authn) — server mirror of
the same canonical bytes.
- [`../PLAN.md`](../PLAN.md) — the staged plan that describes how this
module fits into the wider client.