Skip to content

feat(sdk-go): port Go SDK to developer-platform - #43

Merged
fuller merged 26 commits into
mainfrom
feat/port-go-sdk
Aug 29, 2026
Merged

feat(sdk-go): port Go SDK to developer-platform#43
fuller merged 26 commits into
mainfrom
feat/port-go-sdk

Conversation

@fuller

@fuller fuller commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

BUI-157

Why

Port the Go SDK from api-docs into developer-platform using the deployed API contracts, so Go consumers have a supported, publishable REST and WebSocket SDK from the platform repository.

What changed

  • Added generated REST and WebSocket models plus typed service facades, OAuth PKCE/token refresh, HMAC, RFQ, reconnect, safe retries, pagination, and reconciliation support.
  • Added typed facade access to the full documented query surface for prediction categories and market trades while preserving the existing convenience methods and exact integer serialization.
  • Enforced HTTPS and WSS at the client and transport boundaries; redirect handling is disabled for credentialed requests and authentication headers are cleared before switching strategies.
  • Added fail-closed authentication for private operations and explicit confirmation for destructive REST and WebSocket cancel-all methods.
  • Added bearer re-authentication for safe retries, refresh-owned OAuth cancellation, reconnect token refresh behavior, bounded inputs, header-safe credential validation, and redacted diagnostics.
  • Hardened WebSocket response decoding so sparse or malformed frames cannot retain stale receiver state.
  • Generated artifacts are reproducible from SHA-256-pinned deployed REST, prediction-markets, and WebSocket contracts; the TypeScript WebSocket pin is aligned to the same deployed contract and regenerated.
  • Added CI validation with immutable SHA-pinned GitHub Actions, commit-hash-pinned security analyzer installs, explicit Go 1.23 compatibility coverage, and Linux/386 wire-type coverage.
  • Added extensive service, transport, OAuth, RFQ, WebSocket, race, contract, generation, and release coverage.
  • Upgraded the generator dependency to vulnerability-fixed oapi-codegen v2.7.1.

Validation

  • Root and nested-module go test ./...
  • Root and nested-module go test -race ./...
  • Root and nested-module go vet ./...
  • make generate-check
  • make release-smoke
  • go mod verify across all modules
  • govulncheck: no vulnerabilities reachable from SDK/runtime/demo/Gorilla code; generator-tooling output contains only dependency-only/unreachable advisories
  • gosec: zero findings
  • staticcheck: zero findings
  • TypeScript npm run typecheck and npm test

Live OAuth integration tests were not run because they require credentials. Linux/386 compilation is covered by CI; the local Darwin environment does not provide a usable Linux/386 standard-library toolchain.

Comment thread packages/sdk-go/transport/retries.go Fixed
Comment thread packages/sdk-go/transport/transport.go Fixed
@nostradamus-bot

Copy link
Copy Markdown

Nostradamus Risk Rating — High

The PR introduces packages/sdk-go/auth/ (HMAC-SHA384 signing, bearer token auth, monotonic nonce management with plaintext GEMINI_API_SECRET) and financial transaction services (services/trading.go, services/transfers.go, services/account.go) that directly parallel the Critical security areas in the existing TypeScript SDK and MCP server. Implementation quality is good — credentials are fully redacted in all serialization paths, redirect-forwarding of auth headers is blocked at the transport layer, and static analysis (govulncheck, gosec, staticcheck) passes with zero findings — but the scale (49,768 lines across 136 files) and sensitivity of the new auth/crypto/financial-transaction code warrants a full STRIDE threat model before this SDK is consumed in production contexts.

High/Critical — a full threat model follows.

@sdlc-pr-bot

sdlc-pr-bot Bot commented Aug 28, 2026

Copy link
Copy Markdown

Change Control Evidence Check

Authorization — ✅ Pass

Evidence:

  • Linear ticket: BUI-157
  • Change owner: Andrew Fuller
  • Type of change: Normal
Testing — ✅ Pass

Evidence:

  • Tests from CI checks: ➖ No tests ran
  • Security scans: ✅ Passed
  • Testing summary: ✅ Found in PR description
    • Summary: Comprehensive testing was performed including Go unit tests with race detection, static analysis (vet, gosec, staticcheck), vulnerability scanning (govulncheck with no reachable vulnerabilities), TypeScript type checking and tests, and live OAuth integration testing; all checks passed with zero findings.
  • Evidence link: View run
Approval — ✅ Pass

Evidence:

  • Approver: Jimmy Huang
  • Approval source: GitHub PR Review
  • Approval timestamp: 2026-08-29 20:04 UTC
Segregation of Duties — ✅ Pass

Validated:

  • PR author: Andrew Fuller
  • Commit author(s): fuller
  • Approver: Jimmy Huang
  • Result: Implementer and approver are different people ✅

Last checked: 2026-08-29 20:05 UTC

Re-run Clarissa if tickets, CI, or approvals have changed.

fuller added 5 commits August 28, 2026 13:40
privateWS was constructed with cfg.configErr instead of the derived
privateConfigErr, so PrivateWebSocket() would dial with no auth when
no auth strategy was configured, unlike the private REST client which
already fails closed with ErrAuthenticationRequired.
alignScale (used by Cmp/Add/Sub/Equal/Min/Max) exponentiated an
unbounded scale difference via bigExp10, unlike Div which already caps
at maxDecimalOperationDigits. A single price/amount field parsed with
an extreme scale could force a multi-million-bit big.Int computation
on every comparison. Cap maxDecimalDigits/maxDecimalScale at the same
1,000-digit bound Div already treats as safe, so no parsed Decimal can
produce a scale difference arithmetic isn't bounded to handle cheaply.
@nostradamus-bot

nostradamus-bot Bot commented Aug 28, 2026

Copy link
Copy Markdown

Nostradamus Threat Model - Passed - APPSEC-1561

fuller added 7 commits August 28, 2026 13:52
CalculateLiquidationPrice floored the negative-factor case for long
positions but not shorts, so an extreme maintenance margin rate (e.g.
leverage=1, mmr=3.0) could return a negative liquidation price with a
nil error. CalculateFee also silently zeroed any negative feeBps
instead of computing the maker rebate it represents.
Validate() only checked for a nil TokenSource interface, so a non-nil
interface wrapping a nil TokenFunc (e.g. an unset auth.TokenFunc var)
passed validation and panicked on the first real request instead of
failing fast at construction.
A code review flagged Send() releasing c.mu before WriteMessage as a
race against reconnect's conn swap/close. On investigation this is
not a bug: the Conn interface documents Close as safe to call
concurrently with an in-flight WriteMessage, gorilla/websocket
guarantees it, and holding c.mu for the write's duration was tried and
reverted because it deadlocks Close/state-transition paths against a
slow or blocked write (TestSubscriptionCleanupDoesNotWaitForBlockedWireRequest
exists specifically to guard against that). No behavior change; adds
a comment explaining why the lock is released early.
Sync() matched resting orders to target quotes by iterating the
r.resting map directly. Go's randomized map iteration order meant
that when two resting orders shared side/price/amount (e.g. after a
partial fill), which one got kept vs. cancelled-and-replaced was
nondeterministic, silently breaking the documented FIFO
queue-priority guarantee. Sort resting orders ascending by their
exchange-assigned OrderID (a monotonically increasing uint64) before
matching, so the oldest queued order is always preferred.
encoding/json decodes JSON numbers into interface{} as float64, so an
order ID round-tripped through generic JSON decoding elsewhere in a
caller's application had no matching case and was always rejected
with "order ID is required".
net/http treats an empty Request.Method as GET on the wire (e.g. a
*http.Request built via struct literal, bypassing http.NewRequest's
normalization), but IsSafeMethod("") returned false, so transient
network errors on such a request were never retried even though the
request is a safe, idempotent GET.
APIKey.String() revealed a fixed 4-char prefix and 4-char suffix for
any key longer than 8 characters, so a 10-character key showed 8 of
its 10 characters (80%) in logs/telemetry. Only reveal the
prefix+suffix fingerprint once the key is long enough that it's a
minority of the key (len > 20, so at most ~40% revealed); shorter
keys are now fully redacted like before.
@linear-code

linear-code Bot commented Aug 28, 2026

Copy link
Copy Markdown

BUI-157

@fuller fuller changed the title feat(sdk-go): add secure Go SDK feat(sdk-go): port Go SDK to developer-platform Aug 28, 2026
Comment thread packages/sdk-go/oauth/oauth.go Fixed
Comment thread packages/sdk-go/oauth/oauth.go Fixed

@ximt ximt left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 1 review: four Important correctness/security blockers found. Tests and race checks pass; please address the inline findings before merge.

Comment thread packages/sdk-go/transport/transport.go Outdated
Comment thread packages/sdk-go/auth/hmac.go
Comment thread packages/sdk-go/auth/bearer.go
Comment thread packages/sdk-go/websocket/snapshot_routing.go

@ximt ximt left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 2 re-review: the four prior blockers are fixed, but three new Important correctness and compatibility regressions remain.

Comment thread packages/sdk-go/transport/transport.go
Comment thread packages/sdk-go/services/marketdata.go Outdated
Comment thread packages/sdk-typescript/src/generated/websocket/index.ts
@fuller

fuller commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator Author

@ximt Could you please re-review PR #43 at commit 001d0af? The three Round 2 findings are addressed: caller-supplied public request bodies are preserved, since_tid is exact int64, and AnonymousSchema_152 remains as a deprecated compatibility alias. Targeted and full Go/TypeScript validation passed, and there are zero unresolved inline threads.

@ximt ximt left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 3 re-review: prior correctness and compatibility findings are fixed; one Important forward-regeneration issue remains.

Comment thread packages/sdk-typescript/scripts/generate-ws-types.mjs Outdated

@ximt ximt left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 4 re-review: generator logic is now dynamic, but one Important compatibility-test assertion still hard-codes the old anonymous enum name.

Comment thread packages/sdk-typescript/scripts/websocket-types.test.mjs Outdated
@fuller
fuller requested a review from ximt August 29, 2026 19:57

@ximt ximt left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 5 re-review: the compatibility-test suffix coupling is fixed; no Critical or Important findings remain.

@fuller
fuller merged commit 066f156 into main Aug 29, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants