be830879c66213de693050b5e8697f5826f51ad1
braney
  Wed Sep 2 10:56:33 2026 -0700
ts: serve each ticket sandbox over https as well as http, refs #37867

A parked instance answered only over plain http, so anything a CGI decides from
the request scheme could not be exercised in one at all.  Apache sets HTTPS=on
for a TLS request and cgiServerHttpsIsOn() reads it, so a CGI that branches on
it, such as one deciding whether to mark a cookie Secure, always took the same
branch in a park no matter what was being tested.

Each instance now listens twice: plain http on its registered port, as before,
and https on that port plus 1000, from a self-signed certificate generated once
and shared by every park on the account.  Both listeners serve the same frozen
code, so hitting the pair is the comparison.

Only the http port is in the registry and the https port is derived from it, so
nothing about the existing layout changes and "ts conf NNNNN" adds https to an
instance frozen before this.  http ports are now kept below the start of the
https range so the two cannot overlap.  "ts list" prints both, "ts tunnel"
forwards both, and "ts port NNNNN ssl" gives the https one on its own, which is
how the laptop wrapper asks, rather than repeating the offset in a second file.

diff --git src/utils/ts/README src/utils/ts/README
index 41ab62812a0..788c2aece52 100644
--- src/utils/ts/README
+++ src/utils/ts/README
@@ -35,30 +35,33 @@
   ts sync 37867                   re-freeze after more work on the live sandbox
   ts conf 37867                   rewrite the config only, keeping the freeze
   ts list                         show every parked ticket and its status
   ts tunnel 37867                 open the tunnel and print the browser URL
   ts remove 37867                 stop the httpd and delete the instance
 
 Any hgwdev account can reach a parked instance, because all hgwdev users share
 the machine's loopback:
 
   ssh -N -L PORT:localhost:PORT you@hgwdev.gi.ucsc.edu
   # then open http://localhost:PORT/cgi-bin/hgTracks
 
 There is no password on a parked instance.  It shows only what any hgwdev user
 can already build and query.
 
+Every instance also answers over https, on its port plus 1000.  "ts list" prints
+both numbers, and "ts tunnel" forwards both.  See Notes for why.
+
 Notes
 -----
 
 The freeze is a full copy, not hardlinks.  Hardlinks only freeze a file against
 replacement by rename, so an in-place edit of a live file would also change the
 parked copy.
 
 The CGIs read two htdocs directories.  The frozen htdocs-$USER is the
 DocumentRoot, and the code also reads a sibling ../htdocs for files such as the
 URW fonts.  Each parked instance keeps a symlink to the shared
 /usr/local/apache/htdocs for that reason.  Without it, rendering fails because
 it cannot find a font file.
 
 udc caches remote bigData files by URL.  The live sandbox and every parked
 ticket would otherwise read and write the same trash/udcCache tree, so a cache
@@ -72,23 +75,42 @@
 not have to re-freeze to get a private cache.
 
 The cart cookie is written with no domain on it.  The shared config sets
 central.domain=.ucsc.edu, and a parked instance answers on localhost, so a
 browser drops a cookie scoped to .ucsc.edu.  Every request then gets a fresh
 cart, and each track comes up at its trackDb default rather than the setting you
 chose.  Nothing errors, which is what makes this worth saying out loud: the page
 still renders.  Clicking through a park by hand hides the problem, because
 hgTracks puts the hgsid into the links on its own pages.  A script that
 navigates by URL has nothing to carry, so it quietly measures the defaults and
 reports a clean run.  ts writes an empty central.domain into the frozen hg.conf.
 That leaves the domain attribute off the cookie and makes it host-only, which
 works whether you reach the instance as localhost or as 127.0.0.1.  "ts conf"
 adds it to an instance frozen before ts had it.
 
+Each instance listens twice: plain http on its registered port, and https on
+that port plus 1000.  Both serve the same frozen code.  The point of the pair is
+that some CGI behaviour depends on the scheme, and on a plain-http park you
+cannot see it at all.  Apache sets HTTPS=on only for the https listener, and
+cgiServerHttpsIsOn() reads exactly that, so a CGI deciding whether to mark a
+cookie Secure takes one branch on each port.  Hitting both is then a two-line
+check rather than something you have to reason about.
+
+The certificate is self-signed, made once, and shared by every instance on the
+account.  It lives in ticketSandboxes/ssl.  A browser warns the first time; curl
+needs -k.  That is fine here: a park has no password and is reached through an
+ssh tunnel that is already encrypted, so the certificate exists to turn HTTPS=on
+for the CGIs, not to prove who is serving.
+
+Only the http port is in the registry.  The https port is derived, so nothing
+about the old layout changed and "ts conf" adds https to an instance frozen
+before it existed.  http ports are kept below 49080 so the two ranges cannot
+overlap.
+
 Each instance draws its ticket number faintly across the page background, so you
 always know which frozen browser you are viewing.  ts writes that stylesheet
 into the frozen htdocs and sets browser.style in the frozen hg.conf.
 
 ts does not replace the tip/beta/rel Docker instances.  Those reproduce an exact
 released environment, including the database.
 
 Write-up: https://hgwdev.gi.ucsc.edu/~braney/per-rm-sandboxes.html