v0.1.0:46 — idempotent Connect BTCPay, Go SDK now part of toolchain

Closes the last T1 BTCPay UX gap from V0.2_PLAN. Connect now checks
/v1/admin/btcpay/status first; if a connection exists, returns a
clear "already connected" guidance message pointing the operator at
Disconnect → Connect for re-authorize cases. Without this guard,
re-clicking Connect spawned a new webhook subscription on BTCPay's
side every time, leaving orphan webhooks BTCPay would keep trying
to deliver to.

The Go SDK has been written and verified — all 4 crosscheck tests
pass against the shared tests/crosscheck/vector.json (the same file
the Rust/TS/Python SDKs and the daemon test against). Pure stdlib,
zero third-party dependencies. Hosted in its own repo at
github.com/keysat-xyz/keysat-client-go (private during alpha).

This release IS the 5th-language milestone: daemon + Rust + TS +
Python + Go all agree byte-for-byte on the LIC1 wire format.

Daemon binary unchanged — wrapper-only revision.
This commit is contained in:
Grant
2026-05-08 11:20:17 -05:00
parent 9c5be85c55
commit 763a44bbdd
2 changed files with 58 additions and 1 deletions
+45
View File
@@ -33,6 +33,51 @@ export const configureBtcpay = sdk.Action.withoutInput(
async () => {
const storeData = await store.read().once()
if (!storeData) throw new Error('Store not initialized — restart the service.')
// Idempotency guard: if Keysat is already connected to a BTCPay
// store, re-running Connect would spawn a NEW webhook subscription
// on BTCPay's side (because the authorize flow always registers
// one). That leaves orphan webhooks pointing at this Keysat that
// BTCPay will keep trying to deliver to, and confuses
// reconciliation. Steer the operator to Disconnect first instead.
try {
const statusResp = await adminCall(
LICENSING_URL,
storeData.admin_api_key,
'/v1/admin/btcpay/status',
{ method: 'GET' },
)
if (statusResp.ok) {
const status = (await statusResp.json()) as {
connected?: boolean
store_id?: string | null
base_url?: string | null
}
if (status.connected) {
return {
version: '1',
title: 'BTCPay already connected',
message:
`Keysat is already connected to ` +
`${status.base_url ?? '(unknown URL)'} ` +
`(store ${status.store_id ?? '(unknown id)'}).\n\n` +
`To re-authorize (e.g., switch stores or rotate the API key), ` +
`run "Disconnect BTCPay" first, then re-run "Connect BTCPay". ` +
`Existing license keys, products, and policies are unaffected ` +
`by a Disconnect/Connect cycle.\n\n` +
`If you're seeing connection problems, "Check BTCPay connection" ` +
`also reports wallet / payment-method status that the connect ` +
`flow doesn't surface.`,
result: null,
}
}
}
// Status check failure is non-fatal — fall through to the
// authorize flow. Same UX as before.
} catch (_) {
// Same — non-fatal.
}
const resp = await adminCall(
LICENSING_URL,
storeData.admin_api_key,