feat(shard)!: declare wire protocol 3 #117

Merged
whitlocktech merged 1 commits from chore/protocol-3-cutover into edge 2026-07-30 03:02:26 +00:00
6 changed files with 29 additions and 7 deletions
Showing only changes of commit 779a304173 - Show all commits

View File

@@ -117,7 +117,10 @@ BOT_INTERNAL_KEY=change-me-to-a-long-random-string
# token). These URLs are just defaults; the admin can override them at runtime. # token). These URLs are just defaults; the admin can override them at runtime.
UOLINK_BASE_URL=http://127.0.0.1:8080 UOLINK_BASE_URL=http://127.0.0.1:8080
UOLINK_WS_URL=ws://127.0.0.1:8080/ws UOLINK_WS_URL=ws://127.0.0.1:8080/ws
UOLINK_PROTOCOL=1 # Wire protocol this build speaks (3 = Protocol 3.0). Only a fallback for a site
# with nothing saved yet — the admin panel's pinned value wins — but set it lower
# if you deliberately run an older sidecar.
UOLINK_PROTOCOL=3
# ─── Push notifications (M7) — self-hosted ntfy UnifiedPush relay ─── # ─── Push notifications (M7) — self-hosted ntfy UnifiedPush relay ───
# The `ntfy` compose service and the backend's push fan-out (opt-in notifications # The `ntfy` compose service and the backend's push fan-out (opt-in notifications

View File

@@ -155,7 +155,7 @@ export default function ShardAdmin() {
const [baseUrl, setBaseUrl] = useState('') const [baseUrl, setBaseUrl] = useState('')
const [wsUrl, setWsUrl] = useState('') const [wsUrl, setWsUrl] = useState('')
const [token, setToken] = useState('') const [token, setToken] = useState('')
const [protocol, setProtocol] = useState(1) const [protocol, setProtocol] = useState(3)
const [enabled, setEnabled] = useState(false) const [enabled, setEnabled] = useState(false)
const [busy, setBusy] = useState(false) const [busy, setBusy] = useState(false)
const [msg, setMsg] = useState('') const [msg, setMsg] = useState('')
@@ -172,7 +172,7 @@ export default function ShardAdmin() {
if (!initializedRef.current) { if (!initializedRef.current) {
setBaseUrl(c.baseUrl || '') setBaseUrl(c.baseUrl || '')
setWsUrl(c.wsUrl || '') setWsUrl(c.wsUrl || '')
setProtocol(c.protocol || 1) setProtocol(c.protocol || 3)
setEnabled(c.enabled) setEnabled(c.enabled)
initializedRef.current = true initializedRef.current = true
} }

View File

@@ -359,7 +359,7 @@ CREATE TABLE IF NOT EXISTS uo_link_config (
base_url VARCHAR(255) NULL, base_url VARCHAR(255) NULL,
ws_url VARCHAR(255) NULL, ws_url VARCHAR(255) NULL,
auth_token_enc TEXT NULL, auth_token_enc TEXT NULL,
protocol INT NOT NULL DEFAULT 1, protocol INT NOT NULL DEFAULT 3,
enabled TINYINT(1) NOT NULL DEFAULT 0, enabled TINYINT(1) NOT NULL DEFAULT 0,
status VARCHAR(20) NOT NULL DEFAULT 'disconnected', status VARCHAR(20) NOT NULL DEFAULT 'disconnected',
status_detail VARCHAR(500) NULL, status_detail VARCHAR(500) NULL,
@@ -1397,3 +1397,19 @@ ALTER TABLE mobile_refresh_tokens ADD COLUMN IF NOT EXISTS last_used_at DATETIME
-- trust token. A boolean only — the token is returned over that app→server call -- trust token. A boolean only — the token is returned over that app→server call
-- and never persisted here (only its sha256 lands in trusted_devices). -- and never persisted here (only its sha256 lands in trusted_devices).
ALTER TABLE mobile_auth_sessions ADD COLUMN IF NOT EXISTS trust_device TINYINT(1) NOT NULL DEFAULT 0; ALTER TABLE mobile_auth_sessions ADD COLUMN IF NOT EXISTS trust_device TINYINT(1) NOT NULL DEFAULT 0;
-- Protocol 3.0 cutover: this build speaks wire protocol 3 (world.ruleset,
-- points.board, vendor.listing), so the pinned version an existing install
-- carries has to move with it — a 2 against a v3 sidecar 409s every REST call
-- and closes the WS on ws.hello. MODIFY fixes the column default for installs
-- created before the bump (idempotent, like the other MODIFYs here).
ALTER TABLE uo_link_config MODIFY COLUMN protocol INT NOT NULL DEFAULT 3;
-- The row itself is admin-editable, and schema.sql runs on EVERY boot, so this
-- must be one-shot: an operator who deliberately pins an older sidecar in
-- Admin → Shard has to stay pinned. The marker row in `settings` is what makes
-- it fire once — written after the UPDATE, and on a fresh install (no
-- uo_link_config row yet) it is simply written with nothing to update.
UPDATE uo_link_config SET protocol = 3
WHERE id = 1 AND protocol < 3
AND NOT EXISTS (SELECT 1 FROM settings WHERE `key` = 'uo_link_protocol_3_migrated');
INSERT IGNORE INTO settings (`key`, value) VALUES ('uo_link_protocol_3_migrated', '1');

View File

@@ -7,7 +7,10 @@
const db = require('./uoLinkConfig.db') const db = require('./uoLinkConfig.db')
const secretBox = require('../../utils/secretBox') const secretBox = require('../../utils/secretBox')
const DEFAULT_PROTOCOL = Number(process.env.UOLINK_PROTOCOL) || 1 // The wire protocol this build speaks (link/sidecar/src/main.rs PROTOCOL_VERSION).
// Only used before an admin has saved anything — the stored row wins once it exists,
// and UOLINK_PROTOCOL still overrides for an operator running an older sidecar.
const DEFAULT_PROTOCOL = Number(process.env.UOLINK_PROTOCOL) || 3
function toSafe(row) { function toSafe(row) {
if (!row) { if (!row) {

View File

@@ -58,7 +58,7 @@ async function call(path, { method = 'GET', body } = {}) {
const headers = { const headers = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-UOLink-Version': String(config.protocol || 1), 'X-UOLink-Version': String(config.protocol || 3),
} }
if (config.token) headers.Authorization = `Bearer ${config.token}` if (config.token) headers.Authorization = `Bearer ${config.token}`

View File

@@ -197,7 +197,7 @@ async function connect() {
return return
} }
state.protocol = config.protocol || 1 state.protocol = config.protocol || 3
helloSeen = false helloSeen = false
const url = buildUrl(config.wsUrl, config.token) const url = buildUrl(config.wsUrl, config.token)