Skip to content

Latest commit

 

History

History
25 lines (21 loc) · 10.3 KB

File metadata and controls

25 lines (21 loc) · 10.3 KB

Project agent memory

This file is the project's committed home for project-intrinsic agent knowledge: build, test, release, architecture, and sharp-edge notes that should travel with the code.

  • Add durable project-specific notes here as they are discovered through real work.
  • Secure-chat acknowledgement state is maintained in pkg/edition/java/proxy/chat_queue.go and driven by the handlers in handle_cmd.go/handle_chat.go; it is a direct port of Velocity (ChatQueue, SessionCommandHandler), which is the reference for correct behavior. The LastSeenMessages.Offset += delayedAckCount adjustment is intentional and matches Velocity — not dead code — so validate any change against Velocity's source and a proxy-level reproduction. Key invariant: an UnsignedPlayerCommand (1.20.5+, commands with no signable args — the common path in offline mode) carries NO last-seen update, so it must not be fed into the chat queue (that flushes and discards the player's held acknowledgements) and must be forwarded command-only; a consumed command's ChatAcknowledgement gates on the offset, not the acknowledged bitset. Regression coverage: handle_cmd_ack_test.go.
  • The handshake ServerAddress Gate writes to a backend is also the virtual host the next proxy routes on (Gate behind Connect/Gate Lite is a real topology), so anything appended to it — Forge tokens, forwarding data, TCPShield real IP — must keep the host as the first \0-part. pkg/edition/java/proxy/server.go (handshakeAddr, backendHandshakeBaseHost) owns that invariant; lite.ClearVirtualHost is the consumer that reads the host back out. Upstream Forge scans every \0-part, so a mangled address still works with a single proxy and only breaks one hop down — test the chained form, not just the backend's tolerance (backend_handshake_addresser_test.go, lite/forge_route_test.go).
  • Lite mode pipes the raw connection, so config.Validate() returns early and every full-proxy setting (forwarding, servers/try/forcedHosts, onlineMode, compression, status, announceForge) is inert; warnLiteIgnoredSettings in pkg/edition/java/config/config.go is the list of the ones Gate warns about. Warn only on non-default values and remember DefaultConfig.Forwarding.Mode is legacy, so warning on it would fire for every Lite config, including the shipped pkg/configs/config-lite.yml.
  • PROXY protocol on the client listener is a trust boundary, not a parsing detail: a header is only honored from an upstream in proxyProtocolTrustedProxies (pkg/edition/java/proxy/proxy_protocol.go, defaults in config.DefaultProxyProtocolTrustedProxies), otherwise the peer may not send one at all. Never wrap an accepted connection with a bare proxyproto.NewConn(conn) — that defaults to USE with no deadline, letting any peer assert an arbitrary client IP (ban evasion plus framing a third party) and stall the sniff forever, since the 10s default only applies via proxyproto.Listener. Regression coverage: proxy_protocol_test.go. pkg/edition/bedrock/geyser/geyser.go still wraps bare, guarded only by its loopback-by-default geyserListenAddr.
  • Startup/boot regressions are covered by pkg/gate/gate_startup_test.go, which boots Gate from the shipped config templates (pkg/configs) via LoadConfig + New/Start in go test ./.... Sharp edges it encodes: booting Bedrock is not hermetic (it runs managed Geyser, so only wiring is asserted there); a Connect client with an empty connect.name fetches a random endpoint name over the internet; and newConfigCandidate must deep-copy mutable defaults before parsing so concurrent loads retain independent last-known-good state.
  • Gate publishes through the default-branch .github/workflows/release-publish.yml reusable workflow; the default-branch-only release-please workflow calls it with the selected tag as input, including after its repository-dispatch rerun, so tag-authored workflow steps never receive write credentials. Its release and container paths have two trust zones: release-build/image-build run the checked-out tag with contents: read and stage allowlisted release assets/OCI archives, while fresh publish-release/publish-images jobs check out nothing and alone hold contents: write/packages: write. Keep workflow-level permissions empty, checkout credentials disabled in code-running jobs, and the artifact handoffs explicit; contracts live in ci_write_token_isolation_test.go. The final release guard re-reads GitHub rather than trusting local output (release_asset_verification_test.go) and must remain the publisher's last step.
  • Gate's verified Velocity sync point — which upstream commit is proven to have landed here, and the log of every upstream review since — lives in VELOCITY_SYNC.md at the repo root (shape guarded by velocity_sync_test.go). Read and append there rather than reconstructing it from history; note the file records a verified sync point as of one commit, not general parity with upstream.
  • Releases already published with no assets are repaired by .github/workflows/release-repair.yml (manual dispatch from the default branch, contract pinned by release_repair_workflow_test.go). Its two-job boundary is load-bearing: only build checks out and runs the selected tag with contents: read, while fresh publish has contents: write, checks out nothing, and uploads only the allowlisted artifact; the workflow has no ambient permission and no registry scope, so a backward ghcr.io/minekube/gate:latest retag is unrepresentable. It must live on the default branch and check the tag out, because workflow_dispatch compiles the workflow file at the dispatched ref — a repair input added to ci.yml would not exist on the old tags that need it. The build job pins Go via go-version-file: go.mod of the tag: today's Go is not a neutral substitute for an old tag's, e.g. go vet ./... on v0.48.0 is clean at go1.24.1 and reports two printf diagnostics at go1.26.2, which make test would surface as a false red on source that shipped fine. GoReleaser runs with --skip=publish (assets are uploaded separately with gh release upload --clobber) so a repair never rewrites a historical release's notes or identity — older tags (v0.63.0 and back) predate release: mode: keep-existing and would otherwise be deleted and recreated. Tags whose .goreleaser.yml predates version: 2 (v0.41.1 and older) cannot be repaired by a v2 toolchain; that is a finding to report, not a thing to work around.
  • Config reload correctness must not rely on fsnotify delivery alone: pkg/internal/reload/watch.go reconciles content fingerprints because macOS can miss delete/recreate events. On Windows, fingerprint handles must allow delete sharing or they can make an editor's atomic replacement fail with access denied. Go's os.Rename/MoveFileExW still cannot replace an open destination, so open-handle replacement coverage uses ReplaceFileW. Preserve its single-owner loop and silent-watcher regression coverage when changing debounce or watcher behavior.
  • Bedrock principal v2 (Connect capability bedrock-verified-principal-v2) is consumed source-level from go.minekube.com/connect/bedrockprincipal (pinned as a pseudo-version of the frozen main commit; the spec is moxy commit 396447f, docs/superpowers/plans/2026-07-31-connect-bedrock-go-gate-consumers.md). Gate's generated Watch Session predates frozen fields 6-12, so pkg/util/connectutil/principal.go reads them from the unknown-field region by exact number/wire type; pkg/util/connectutil/config/principal.go + setup_client.go verify exactly once and apply only the verifier-produced profile, advertise the capability only when ReadinessState.Ready(), and reject envelope/Bedrock proposals instead of downgrading to proposed profile data. Never log proposals, XUIDs, usernames, or player addresses in these paths (guarded by setup_client_log_privacy_test.go, geyser_log_privacy_test.go). Linked Java identity is applied only from: (a) a verified principal (signed, Connect path — always authoritative), or (b) opt-in via backendFloodgate.enabled (default off) from the AES-authenticated Floodgate handshake triplet cross-checked against the connection's own Floodgate bedrock UUID, or (c) the same opt-in, the GeyserMC global link API as fallback when the handshake carries no triplet (pkg/edition/bedrock/geyser/geyser.go onGameProfile; parse in floodgate.ParseLinkedPlayer, cross-check via BedrockData.FloodgateJavaUuid - note that is new UUID(0, xuid), NOT the SHA-1 JavaUuid; API lookup in ProfileManager.GetLinkedAccount, the official GlobalPlayerLinking service). Never promote identity from the GeyserMC API outside that opt-in gate. Note: go.minekube.com/geyserlite resolves through a tags-only module proxy, so it cannot be bumped to untagged commits; its verified-ingress API waits for a real release.
  • Bedrock gamertags cross into Java profiles in pkg/edition/bedrock/geyser/geyser.go; the rendered usernameFormat must be normalized to Java's 16-character ASCII username alphabet there. Modern Paper rejects the traditional dot prefix and Bedrock spaces before login, so preserve javaCompatibleUsername and username_test.go when changing profile construction.
  • Java profile properties encode the optional signature with an always-present boolean. WriteProperties must write false for unsigned properties; omitting it shifts the following 26.2 login SessionID into the property decoder and breaks Geyser. Preserve the signed and unsigned wire fixtures in writer_test.go.
  • HTTP config writes share the transactional reload boundary in pkg/gate/gate.go: GetConfig returns an opaque version, ApplyConfig requires it and may target edits with JSON Merge Patch, and only route changes in an already-enabled Java Lite config are live-safe. Keep pkg/gate/api_handlers.go on Gate.ApplyLiveConfigIfVersion; broad pointer mutation plus reload.FireConfigUpdate can claim startup-bound fields changed when the running listeners and forwarding mode did not.

Maintaining this file

Keep this file for knowledge useful to almost every future agent session in this project. Do not repeat what the codebase already shows; point to the authoritative file or command instead. Prefer rewriting or pruning existing entries over appending new ones. When updating this file, preserve this bar for all agents and keep entries concise.