32cd2100d9dd11140705c848850e86c4f79191c4
max
  Tue Sep 1 07:02:04 2026 -0700
Reusable "view snapshot" sessions for durable, minimal Share-a-link links

"Share a link" could only share the whole cart: it saved a session holding
every track, setting and position, which bloated the central db and leaked
the sharer's unrelated tracks to whoever opened the link.  Anonymous share
sessions (under the reserved user "l") were also never reaped, so they
accumulated forever, and their 8-char names were generated client-side with
no uniqueness check, so two shares could collide and silently overwrite.

Adds a lightweight "snapshot session" facility (lib/snapshotSession.c): a
snapshot stores only the handful of cart variables a feature declares (a
registered snapshotType, e.g. "blat" -> {db, blatLastBigBed}), moves only
those variables' trash files into durable sessionData storage, and is saved
under a "__"-prefixed name.  The "__" marks it machine-made: hidden from the
My Sessions list by default and eligible for reaping.  For the anonymous "l"
owner the durable files fan out over two extra hash levels so one directory
never fills with millions of entries.

Every anonymous link now shares one server-side name generator
(snapshotNewName): a unique (db-checked), crypto-strong, "__"-prefixed token,
so tokens never collide.  The hgSession doSaveSessionJson endpoint gained
hgS_snapshotType (save a minimal snapshot rather than the whole cart) and
hgS_doAnonName (reserve a unique anonymous name without saving, so the
top-right dialog can preview the exact link before it is created).

Wired three callers to the facility:
- hgc htcBlatAlign "Share a link": a minimal "blat" snapshot instead of a
full-cart anonymous session.
- hgBlat results "Share a link": creates a "blat" snapshot on click and
reveals its ?u=&s= reopen link (rebuilt from the durable bigPsl by the
existing doShareReopen), replacing the trash-only reveal.
- top-right "Share a link": anonymous links use the reserved server name;
logged-in named shares are unchanged.

snapshotReaper (hg/utils) garbage-collects abandoned anonymous snapshots: it
deletes user "l" "__" rows whose lastUse is older than the TTL (hg.conf
snapshot.ttlDays, default ~4 years) and removes their durable files.  lastUse
is bumped on every open by the existing session load, so a link stays alive
as long as it is used.  Meant to run from the trash-cleaner cron.

Also folds in the recent Share-dialog work in these files: the auto share
name is a short "_" prefix instead of "share_", the dialog previews the link
and creates it only when the button is clicked (no orphan session just from
opening the dialog), an optional name field with an overwrite warning, and
the anonymous save path forces the reap-eligible "__" name.

refs #38197

diff --git src/hg/lib/makefile src/hg/lib/makefile
index 78bbe736607..29d68d184ae 100644
--- src/hg/lib/makefile
+++ src/hg/lib/makefile
@@ -1,86 +1,86 @@
 kentSrc = ../..
 include ../../inc/localEnvironment.mk
 include ../../inc/common.mk
 XINC = -I$(MYSQLINC)
 
 O=adjacency.o affyPairs.o agpFrag.o agpGap.o alignSeqSizes.o altGraphX.o \
   asmAlias.o asmEquivalent.o assemblySummary.o assemblyList.o autoUpgrade.o axtInfo.o \
   barChartBed.o barChartCategory.o barChartUi.o bed.o bed12Source.o \
   bedDetail.o bedMethyl.o bigBedFind.o bigBedLabel.o bigChain.o bigDbSnp.o bigGenePred.o bigLink.o \
   bigPsl.o blastTab.o blatServers.o blatShare.o borf.o botDelay.o cart.o cartDb.o cartTrackDb.o cdsEvidence.o cdsOrtho.o cdsPick.o \
   cgapSage/cgapSage.o cgapSage/cgapSageLib.o cgiApoptosis.o chainCart.o chainDb.o chainLink.o chainNet.o \
   chainNetDbLoad.o chromAlias.o chromBins.o chromGraph.o chromGraphFactory.o chromInfo.o chromInserts.o \
   chromKeeper.o clonePos.o cpgIsland.o ctgPos.o customAdjacency.o customComposite.o customFactory.o \
   customPp.o customTrack.o cv.o cytoBand.o dbDb.o decoration.o decoratorUi.o defaultDb.o dnaMotifSql.o dupTrack.o \
   encode/encodeErge.o encode/encodeErgeHssCellLines.o encode/encodeExp.o encode/encodeHapMapAlleleFreq.o \
   encode/encodeIndels.o encode/encodePeak.o encode/encodeRegionInfoCustom.o encode/encodeRna.o \
   encode/encodeStanfordPromoters.o encode/encodeStanfordPromotersAverage.o encode/peptideMapping.o \
   encode/wgEncodeGencodeAnnotationRemark.o encode/wgEncodeGencodeAttrs.o encode/wgEncodeGencodeEntrezGene.o \
   encode/wgEncodeGencodeExonSupport.o encode/wgEncodeGencodeGeneSource.o  encode/wgEncodeGencodeGeneSymbol.o encode/wgEncodeGencodePdb.o \
   encode/wgEncodeGencodePubMed.o encode/wgEncodeGencodeRefSeq.o encode/wgEncodeGencodeTag.o \
   encode/wgEncodeGencodeTranscriptSource.o encode/wgEncodeGencodeTranscriptSupport.o \
   encode/wgEncodeGencodeTranscriptionSupportLevel.o encode/wgEncodeGencodeUniProt.o encode3/encode3Valid.o \
   estOrientInfo.o expData.o exportedDataHubs.o expRecord.o facetField.o facetedTable.o featureBits.o findKGAlias.o \
   fakeCurl.o \
   findKGProtAlias.o gbSeq.o gbExtFile.o gcPercent.o genark.o genbank.o genbankBlackList.o gencodeTracksCommon.o gencodeAttrs.o gencodeToRefSeq.o geneGraph.o \
   genePred.o genePredReader.o geoMirror.o ggCluster.o ggDump.o ggGraph.o ggMrnaAli.o ggTypes.o glDbRep.o \
   googleAnalytics.o gpFx.o grp.o gtexAse.o gtexDonor.o gtexGeneBed.o gtexInfo.o gtexSample.o \
   gtexSampleData.o gtexTissue.o gtexTissueMedian.o gtexUi.o hCommon.o hPrint.o hVarSubst.o \
   hapmapAllelesOrtho.o hapmapPhaseIIISummary.o hapmapSnps.o hdb.o hgColors.o hgConfig.o hgFind.o \
   hgFindSpec.o hgFindSpecCustom.o hgHgvs.o hgHgvsParse.o hgMaf.o hgRelate.o hic.o hicUi.o hubConnect.o hubPublic.o \
   hubSearchText.o hubSpace.o hubSpaceKeys.o hubSpaceQuotas.o hui.o imageClone.o indelShift.o interact.o interactUi.o itemAttr.o jksql.o joiner.o \
   jsHelper.o kgAlias.o kgProtAlias.o kgXref.o knownInfo.o knownMore.o lav.o lfs.o liftOver.o liftOverChain.o \
   liftUp.o longRange.o lrg.o lsSnpPdb.o mafFrames.o mafGene.o mafSummary.o myVariants.o myVariantsShare.o mdb.o \
   microarray.o minChromSize.o namedSessionDb.o netAlign.o netCart.o pepPred.o pgPhenoAssoc.o pgSnp.o pslReader.o qaSeq.o \
   quickLift.o quickLiftChain.o rankProp.o refLink.o refSeqStatus.o rikenCluster.o rmskAlign.o rmskJoined.o rmskOut.o rmskOut2.o rnaGene.o sage.o sageCounts.o samAlignment.o \
-  sample.o sanger22extra.o scoredRef.o sessionData.o seqWindow.o snakeUi.o snp.o snpExceptions.o soTerm.o spDb.o sqlProg.o \
+  sample.o sanger22extra.o scoredRef.o sessionData.o snapshotSession.o seqWindow.o snakeUi.o snp.o snpExceptions.o soTerm.o spDb.o sqlProg.o \
   sqlSanity.o stanMad.o stsAlias.o stsInfo2.o stsMap.o stsMarker.o tableStatus.o tablesTables.o \
   tagRepo.o tfbsCons.o tigrCmrGene.o traceInfo.o trackDb.o trackDbCache.o trackDbCustom.o trackHub.o \
   trackVersion.o trashDir.o txAliDiff.o txCluster.o txCommon.o txEdgeBed.o txGraph.o txInfo.o txRnaAccs.o \
   userdata.o variantProjector.o vcfUi.o vegaInfo.o web.o wigAsciiToBinary.o wigDataStream.o wiggle.o wiggleCart.o \
   wiggleUtils.o wikiLink.o wikiTrack.o bigRmskAlignBed.o bigRmskBed.o
 
 ifeq (${USE_HIC}, 1)
   O += straw.o
 endif
 
 ifeq (${GBROWSE}, 1)
   GBROWSE_D=-DGBROWSE
 else
   GBROWSE_D=
 endif
 
 %.o: %.c
 	${CC} ${COPT} ${CFLAGS} ${GBROWSE_D} ${LOWELAB_DEFS} ${HG_DEFS} ${HG_WARN} ${HG_INC} ${XINC} -o $@ -c $<
 
 ../../lib/$(MACHTYPE)/jkhgap.a: $(O) libDir
 	ar rcus ../../lib/$(MACHTYPE)/jkhgap.a $(O)
 
 libDir:
 	mkdir -p ../../lib/$(MACHTYPE)
 
 jWestHeader.h: jWestHeader.html
 	sed -e 's/\\/\\\\/g; s/"/\\"/g; s/^/"/; s/$$/\\n"/;' jWestHeader.html > jWestHeader.h
 
 jWestBanner.h: jWestBanner.html
 	sed -e 's/\\/\\\\/g; s/"/\\"/g; s/^/"/; s/$$/\\n"/;' jWestBanner.html > jWestBanner.h
 
 web.o: jWestHeader.h jWestBanner.h
 
 straw.o: straw/straw.cpp straw/straw.h straw/cStraw.cpp
 	cd straw && ${MAKE} straw
 
 clean:
 	rm -f $(O);
 	rm -f jWestHeader.h jWestBanner.h
 	rm -f ../../lib/$(MACHTYPE)/jkhgap.a;
 	cd straw && ${MAKE} clean
 
 ctags:
 	ctags *.c *.h ../inc/*.h ../lib/*.c ../../inc/*.h ../../lib/*.c ../inc/encode/*.h ../lib/encode/*.c
 
 tags:
 	etags *.c *.h ../inc/*.h ../lib/*.c ../../inc/*.h ../../lib/*.c ../inc/encode/*.h ../lib/encode/*.c
 
 test:
 	cd tests && ${MAKE} test