14ecff0730596f8be147b255afc8acb99e9095e2
max
  Fri Sep 18 09:06:06 2026 -0700
hgHubConnect: an api key made on one geo mirror now works on all of them, behind syncHubApiKeys

#Preview2 week - bugs introduced now will need a build patch to fix
#Preview2 week - bugs introduced now will need a build patch to fix
Api keys live in the hgcentral of whichever mirror issued them, so a key made on
genome.ucsc.edu was rejected on genome-euro and genome-asia, and the botDelay
error told the user to go and make a second one. hgHubConnect now tells the other
nodes about a key as soon as it generates or revokes one: cjGenerateApiKey and
cjRevokeApiKey call syncApiKeyToOtherNodes(), which posts a hgHubSyncApiKey
cartJson request to each peer, and cjSyncApiKey() on the far side writes it into
that mirror's own table.

The peers come from hgcentral.gbNode via the new geoMirrorNotifyOtherNodes(), and
the request is signed with login.cookieSalt, which all of a site's mirrors already
share - it is what makes the login cookie verifiable on each of them. So neither a
list of mirror addresses nor a new shared secret needs provisioning. The notify is
best effort: a peer that is down is warn()ed about and skipped, never failing the
local generate or revoke, which has already committed by then.

All of it is off unless hg.conf says syncHubApiKeys=on. With the gate off the
sender returns at once, the receiver refuses the request outright rather than
merely being unreachable, and botDelay keeps printing the old server-specific
wording, which off is still the truth. Registered in hgConfCatalog.py as a gate,
to be flipped once this is released.

refs #38323

diff --git src/hg/hgHubConnect/hgHubConnect.h src/hg/hgHubConnect/hgHubConnect.h
index f8984cca0f0..d8ed20243fe 100644
--- src/hg/hgHubConnect/hgHubConnect.h
+++ src/hg/hgHubConnect/hgHubConnect.h
@@ -1,45 +1,50 @@
 /* hgHubConnect - User interfaces for connecting and managing track hubs */
 
 /* Copyright (C) 2008 The Regents of the University of California 
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #ifndef HGHUBCONNECT_H
 #define HGHUBCONNECT_H
 
 #include "cart.h"
 #include "cartJson.h"
 
 //extern struct cart *cart;	/* This holds cgi and other variables between clicks. */
 
 // the variables for various track hub wizard methods:
 #define hgHubGetHubSpaceUIState "getHubSpaceUIState"
 #define hgHubDeleteFile "deleteFile"
 #define hgHubCreateHub "createHub"
 #define hgHubEditHub "editHub"
 #define hgHubMoveFile "moveFile"
 #define hgHubGenerateApiKey "generateApiKey"
 #define hgHubRevokeApiKey "revokeApiKey"
+#define hgHubSyncApiKey "hgHubSyncApiKey"
 
 void cjRevokeApiKey(struct cartJson *cj, struct hash *paramHash);
 /* Remove any api keys for the user */
 
 void cjGenerateApiKey(struct cartJson *cj, struct hash *paramHash);
 /* Make a random (but not crypto-secure api key for use of hubtools to upload to hubspace */
 
+void cjSyncApiKey(struct cartJson *cj, struct hash *paramHash);
+/* Adopt an api key (or a revocation) that a peer geo mirror is telling us about.  Only ever
+ * called by another mirror's geoMirrorNotifyOtherNodes(), never by a browser. */
+
 void doRemoveFile(struct cartJson *cj, struct hash *paramHash);
 /* Process the request to remove a file */
 
 void doMoveFile(struct cartJson *cj, struct hash *paramHash);
 /* Move a file to a new hub */
 
 void getHubSpaceUIState(struct cartJson *cj, struct hash *paramHash);
 /* Get all the data we need to make a users hubSpace UI table. The cartJson library
  * deals with printing the json */
 
 void doEditHub(struct cartJson *cj, struct hash *paramHash);
 /* Edit the hub.txt for a hub */
 
 void doTrackHubWizard(char *database);
 /* Print out the html to allow a user to upload some files from their machine to us */
 
 #endif /* HGHUBCONNECT_H */