Login
Hex Artifact Content
Login

Artifact 959ef8cc0522f978c613068065f65bb1eee77ddf697d6da5830b0be1226cbcf6:

Attachment "scripts-for-review-3.tar" to tech note [bae6999eb9] added by opus-ocx2 2026-10-29 15:43:07.
0000: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69  scripts-for-revi
0010: 65 77 2d 33 2f 00 00 00 00 00 00 00 00 00 00 00  ew-3/...........
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0060: 00 00 00 00 30 30 30 30 37 35 35 00 30 30 30 30  ....0000755.0000
0070: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30  000.0000000.0000
0080: 30 30 30 30 30 30 30 00 31 35 32 37 30 36 36 34  0000000.15270664
0090: 36 31 33 00 30 31 33 35 30 30 00 20 35 00 00 00  613.013500. 5...
00a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0100: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00  .ustar  .root...
0110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0120: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00  .........root...
0130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
01a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
01b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
01c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
01d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
01e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
01f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0200: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69  scripts-for-revi
0210: 65 77 2d 33 2f 72 65 63 65 69 70 74 73 2e 63 72  ew-3/receipts.cr
0220: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0260: 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30  ....0000644.0000
0270: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30  000.0000000.0000
0280: 30 30 32 33 30 30 34 00 31 35 32 37 30 36 36 34  0023004.15270664
0290: 36 31 33 00 30 31 35 36 34 33 00 20 30 00 00 00  613.015643. 0...
02a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0300: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00  .ustar  .root...
0310: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0320: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00  .........root...
0330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0350: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0360: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0390: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
03a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
03b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
03c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
03d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
03e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
03f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0400: 23 20 41 67 65 6e 74 73 3a 20 64 6f 20 6e 6f 74  # Agents: do not
0410: 20 63 68 61 6e 67 65 20 74 68 69 73 20 66 69 6c   change this fil
0420: 65 20 6f 6e 20 61 6e 79 20 62 72 61 6e 63 68 20  e on any branch 
0430: 77 69 74 68 6f 75 74 20 74 68 65 20 6d 61 69 6e  without the main
0440: 74 61 69 6e 65 72 27 73 20 65 78 70 6c 69 63 69  tainer's explici
0450: 74 20 61 70 70 72 6f 76 61 6c 20 6f 6e 20 74 68  t approval on th
0460: 65 20 72 65 63 6f 72 64 2e 0a 23 20 54 68 65 20  e record..# The 
0470: 72 65 63 65 69 70 74 20 73 65 72 76 69 63 65 3a  receipt service:
0480: 20 73 61 79 73 20 77 68 6f 20 46 6f 73 73 69 6c   says who Fossil
0490: 20 72 65 63 65 69 76 65 64 20 65 61 63 68 20 61   received each a
04a0: 72 74 69 66 61 63 74 20 66 72 6f 6d 2c 20 72 65  rtifact from, re
04b0: 61 64 69 6e 67 20 74 68 65 20 73 65 72 76 65 64  ading the served
04c0: 20 72 65 70 6f 73 69 74 6f 72 69 65 73 20 72 65   repositories re
04d0: 61 64 2d 6f 6e 6c 79 2e 0a 23 20 60 73 74 61 72  ad-only..# `star
04e0: 74 2d 6f 63 78 20 72 65 63 65 69 70 74 73 60 20  t-ocx receipts` 
04f0: 72 75 6e 73 20 69 74 20 69 6e 20 61 20 73 61 6e  runs it in a san
0500: 64 62 6f 78 20 68 6f 6c 64 69 6e 67 20 6f 6e 6c  dbox holding onl
0510: 79 20 74 68 65 20 74 77 6f 20 70 61 74 68 73 20  y the two paths 
0520: 62 65 6c 6f 77 2c 20 73 6f 20 69 74 20 72 65 61  below, so it rea
0530: 64 73 20 6e 6f 20 73 65 74 74 69 6e 67 73 20 66  ds no settings f
0540: 69 6c 65 2e 0a 23 20 45 61 63 68 20 63 6f 6e 6e  ile..# Each conn
0550: 65 63 74 69 6f 6e 3a 20 74 68 65 20 6c 61 75 6e  ection: the laun
0560: 63 68 65 72 27 73 20 69 64 65 6e 74 69 74 79 20  cher's identity 
0570: 6c 69 6e 65 2c 20 74 68 65 6e 20 74 68 65 20 63  line, then the c
0580: 61 6c 6c 65 72 27 73 20 72 65 71 75 65 73 74 20  aller's request 
0590: 6c 69 6e 65 3b 20 6f 6e 65 20 61 6e 73 77 65 72  line; one answer
05a0: 20 6c 69 6e 65 20 62 61 63 6b 2e 0a 53 4f 43 4b   line back..SOCK
05b0: 45 54 20 20 20 20 20 20 20 3d 20 22 2f 72 75 6e  ET       = "/run
05c0: 2f 6f 63 78 2f 72 65 63 65 69 70 74 73 2e 73 6f  /ocx/receipts.so
05d0: 63 6b 22 20 23 20 77 68 65 72 65 20 74 68 65 20  ck" # where the 
05e0: 6c 61 75 6e 63 68 65 72 20 63 6f 6e 6e 65 63 74  launcher connect
05f0: 73 0a 52 45 50 4f 53 49 54 4f 52 49 45 53 20 3d  s.REPOSITORIES =
0600: 20 22 2f 72 65 70 6f 73 69 74 6f 72 69 65 73 22   "/repositories"
0610: 20 20 20 20 20 20 20 20 20 20 23 20 74 68 65 20            # the 
0620: 73 65 72 76 65 64 20 72 65 70 6f 73 69 74 6f 72  served repositor
0630: 69 65 73 2c 20 72 65 61 64 2d 6f 6e 6c 79 0a 52  ies, read-only.R
0640: 45 41 44 5f 57 49 54 48 49 4e 20 20 3d 20 32 2e  EAD_WITHIN  = 2.
0650: 73 65 63 6f 6e 64 73 20 20 20 20 20 20 20 20 20  seconds         
0660: 20 20 20 20 20 20 20 23 20 62 6f 74 68 20 72 65         # both re
0670: 71 75 65 73 74 20 6c 69 6e 65 73 20 6d 75 73 74  quest lines must
0680: 20 61 72 72 69 76 65 20 62 79 20 74 68 65 6e 0a   arrive by then.
0690: 4c 4f 43 4b 5f 57 41 49 54 20 20 20 20 3d 20 32  LOCK_WAIT    = 2
06a0: 2e 73 65 63 6f 6e 64 73 20 20 20 20 20 20 20 20  .seconds        
06b0: 20 20 20 20 20 20 20 20 23 20 74 68 65 20 6c 6f          # the lo
06c0: 6e 67 65 73 74 20 77 61 69 74 20 77 68 69 6c 65  ngest wait while
06d0: 20 46 6f 73 73 69 6c 20 68 6f 6c 64 73 20 61 20   Fossil holds a 
06e0: 72 65 70 6f 73 69 74 6f 72 79 20 6c 6f 63 6b 65  repository locke
06f0: 64 0a 0a 72 65 71 75 69 72 65 20 22 6a 73 6f 6e  d..require "json
0700: 22 0a 72 65 71 75 69 72 65 20 22 73 6f 63 6b 65  ".require "socke
0710: 74 22 0a 72 65 71 75 69 72 65 20 22 75 72 69 22  t".require "uri"
0720: 0a 0a 23 20 54 68 65 20 70 61 72 74 73 20 6f 66  ..# The parts of
0730: 20 53 51 4c 69 74 65 27 73 20 43 20 69 6e 74 65   SQLite's C inte
0740: 72 66 61 63 65 20 75 73 65 64 20 68 65 72 65 2e  rface used here.
0750: 0a 40 5b 4c 69 6e 6b 28 22 73 71 6c 69 74 65 33  .@[Link("sqlite3
0760: 22 29 5d 0a 6c 69 62 20 4c 69 62 53 51 4c 69 74  ")].lib LibSQLit
0770: 65 33 0a 20 20 66 75 6e 20 6f 70 65 6e 5f 76 32  e3.  fun open_v2
0780: 20 3d 20 73 71 6c 69 74 65 33 5f 6f 70 65 6e 5f   = sqlite3_open_
0790: 76 32 28 70 61 74 68 20 3a 20 55 49 6e 74 38 2a  v2(path : UInt8*
07a0: 2c 20 64 62 20 3a 20 56 6f 69 64 2a 2a 2c 20 66  , db : Void**, f
07b0: 6c 61 67 73 20 3a 20 49 6e 74 33 32 2c 20 76 66  lags : Int32, vf
07c0: 73 20 3a 20 55 49 6e 74 38 2a 29 20 3a 20 49 6e  s : UInt8*) : In
07d0: 74 33 32 0a 20 20 66 75 6e 20 63 6c 6f 73 65 5f  t32.  fun close_
07e0: 76 32 20 3d 20 73 71 6c 69 74 65 33 5f 63 6c 6f  v2 = sqlite3_clo
07f0: 73 65 5f 76 32 28 64 62 20 3a 20 56 6f 69 64 2a  se_v2(db : Void*
0800: 29 20 3a 20 49 6e 74 33 32 0a 20 20 66 75 6e 20  ) : Int32.  fun 
0810: 62 75 73 79 5f 74 69 6d 65 6f 75 74 20 3d 20 73  busy_timeout = s
0820: 71 6c 69 74 65 33 5f 62 75 73 79 5f 74 69 6d 65  qlite3_busy_time
0830: 6f 75 74 28 64 62 20 3a 20 56 6f 69 64 2a 2c 20  out(db : Void*, 
0840: 6d 69 6c 6c 69 73 65 63 6f 6e 64 73 20 3a 20 49  milliseconds : I
0850: 6e 74 33 32 29 20 3a 20 49 6e 74 33 32 0a 20 20  nt32) : Int32.  
0860: 66 75 6e 20 70 72 65 70 61 72 65 5f 76 32 20 3d  fun prepare_v2 =
0870: 20 73 71 6c 69 74 65 33 5f 70 72 65 70 61 72 65   sqlite3_prepare
0880: 5f 76 32 28 64 62 20 3a 20 56 6f 69 64 2a 2c 20  _v2(db : Void*, 
0890: 73 71 6c 20 3a 20 55 49 6e 74 38 2a 2c 20 73 69  sql : UInt8*, si
08a0: 7a 65 20 3a 20 49 6e 74 33 32 2c 20 73 74 61 74  ze : Int32, stat
08b0: 65 6d 65 6e 74 20 3a 20 56 6f 69 64 2a 2a 2c 20  ement : Void**, 
08c0: 74 61 69 6c 20 3a 20 55 49 6e 74 38 2a 2a 29 20  tail : UInt8**) 
08d0: 3a 20 49 6e 74 33 32 0a 20 20 66 75 6e 20 62 69  : Int32.  fun bi
08e0: 6e 64 5f 74 65 78 74 20 3d 20 73 71 6c 69 74 65  nd_text = sqlite
08f0: 33 5f 62 69 6e 64 5f 74 65 78 74 28 73 74 61 74  3_bind_text(stat
0900: 65 6d 65 6e 74 20 3a 20 56 6f 69 64 2a 2c 20 69  ement : Void*, i
0910: 6e 64 65 78 20 3a 20 49 6e 74 33 32 2c 20 74 65  ndex : Int32, te
0920: 78 74 20 3a 20 55 49 6e 74 38 2a 2c 20 73 69 7a  xt : UInt8*, siz
0930: 65 20 3a 20 49 6e 74 33 32 2c 20 64 65 73 74 72  e : Int32, destr
0940: 75 63 74 6f 72 20 3a 20 56 6f 69 64 2a 29 20 3a  uctor : Void*) :
0950: 20 49 6e 74 33 32 0a 20 20 66 75 6e 20 73 74 65   Int32.  fun ste
0960: 70 20 3d 20 73 71 6c 69 74 65 33 5f 73 74 65 70  p = sqlite3_step
0970: 28 73 74 61 74 65 6d 65 6e 74 20 3a 20 56 6f 69  (statement : Voi
0980: 64 2a 29 20 3a 20 49 6e 74 33 32 0a 20 20 66 75  d*) : Int32.  fu
0990: 6e 20 63 6f 6c 75 6d 6e 5f 63 6f 75 6e 74 20 3d  n column_count =
09a0: 20 73 71 6c 69 74 65 33 5f 63 6f 6c 75 6d 6e 5f   sqlite3_column_
09b0: 63 6f 75 6e 74 28 73 74 61 74 65 6d 65 6e 74 20  count(statement 
09c0: 3a 20 56 6f 69 64 2a 29 20 3a 20 49 6e 74 33 32  : Void*) : Int32
09d0: 0a 20 20 66 75 6e 20 63 6f 6c 75 6d 6e 5f 74 65  .  fun column_te
09e0: 78 74 20 3d 20 73 71 6c 69 74 65 33 5f 63 6f 6c  xt = sqlite3_col
09f0: 75 6d 6e 5f 74 65 78 74 28 73 74 61 74 65 6d 65  umn_text(stateme
0a00: 6e 74 20 3a 20 56 6f 69 64 2a 2c 20 63 6f 6c 75  nt : Void*, colu
0a10: 6d 6e 20 3a 20 49 6e 74 33 32 29 20 3a 20 55 49  mn : Int32) : UI
0a20: 6e 74 38 2a 0a 20 20 66 75 6e 20 66 69 6e 61 6c  nt8*.  fun final
0a30: 69 7a 65 20 3d 20 73 71 6c 69 74 65 33 5f 66 69  ize = sqlite3_fi
0a40: 6e 61 6c 69 7a 65 28 73 74 61 74 65 6d 65 6e 74  nalize(statement
0a50: 20 3a 20 56 6f 69 64 2a 29 20 3a 20 49 6e 74 33   : Void*) : Int3
0a60: 32 0a 20 20 66 75 6e 20 65 72 72 6d 73 67 20 3d  2.  fun errmsg =
0a70: 20 73 71 6c 69 74 65 33 5f 65 72 72 6d 73 67 28   sqlite3_errmsg(
0a80: 64 62 20 3a 20 56 6f 69 64 2a 29 20 3a 20 55 49  db : Void*) : UI
0a90: 6e 74 38 2a 0a 65 6e 64 0a 0a 23 20 54 68 65 20  nt8*.end..# The 
0aa0: 6f 6e 6c 79 20 71 75 65 72 69 65 73 2e 20 41 20  only queries. A 
0ab0: 63 61 6c 6c 65 72 27 73 20 76 61 6c 75 65 20 69  caller's value i
0ac0: 73 20 62 6f 75 6e 64 20 74 6f 20 3f 31 2c 20 6e  s bound to ?1, n
0ad0: 65 76 65 72 20 77 72 69 74 74 65 6e 20 69 6e 74  ever written int
0ae0: 6f 20 74 68 65 20 53 51 4c 2e 0a 41 52 54 49 46  o the SQL..ARTIF
0af0: 41 43 54 20 3d 20 22 53 45 4c 45 43 54 20 62 2e  ACT = "SELECT b.
0b00: 72 69 64 2c 20 62 2e 72 63 76 69 64 2c 20 45 58  rid, b.rcvid, EX
0b10: 49 53 54 53 28 53 45 4c 45 43 54 20 31 20 46 52  ISTS(SELECT 1 FR
0b20: 4f 4d 20 70 72 69 76 61 74 65 20 70 20 57 48 45  OM private p WHE
0b30: 52 45 20 70 2e 72 69 64 3d 62 2e 72 69 64 29 2c  RE p.rid=b.rid),
0b40: 20 75 2e 6c 6f 67 69 6e 2c 20 75 2e 63 61 70 20   u.login, u.cap 
0b50: 46 52 4f 4d 20 62 6c 6f 62 20 62 20 22 20 5c 0a  FROM blob b " \.
0b60: 20 20 20 20 20 20 20 20 20 20 20 22 4c 45 46 54             "LEFT
0b70: 20 4a 4f 49 4e 20 72 63 76 66 72 6f 6d 20 72 20   JOIN rcvfrom r 
0b80: 4f 4e 20 72 2e 72 63 76 69 64 3d 62 2e 72 63 76  ON r.rcvid=b.rcv
0b90: 69 64 20 4c 45 46 54 20 4a 4f 49 4e 20 75 73 65  id LEFT JOIN use
0ba0: 72 20 75 20 4f 4e 20 75 2e 75 69 64 3d 72 2e 75  r u ON u.uid=r.u
0bb0: 69 64 20 57 48 45 52 45 20 62 2e 75 75 69 64 3d  id WHERE b.uuid=
0bc0: 3f 31 20 41 4e 44 20 62 2e 73 69 7a 65 3e 3d 30  ?1 AND b.size>=0
0bd0: 22 0a 55 53 45 52 20 20 20 20 20 3d 20 22 53 45  ".USER     = "SE
0be0: 4c 45 43 54 20 63 61 70 20 46 52 4f 4d 20 75 73  LECT cap FROM us
0bf0: 65 72 20 57 48 45 52 45 20 6c 6f 67 69 6e 3d 3f  er WHERE login=?
0c00: 31 22 0a 44 45 46 41 55 4c 54 53 20 3d 20 22 53  1".DEFAULTS = "S
0c10: 45 4c 45 43 54 20 6c 6f 67 69 6e 2c 20 63 61 70  ELECT login, cap
0c20: 20 46 52 4f 4d 20 75 73 65 72 20 57 48 45 52 45   FROM user WHERE
0c30: 20 6c 6f 67 69 6e 20 49 4e 20 28 27 6e 6f 62 6f   login IN ('nobo
0c40: 64 79 27 2c 27 61 6e 6f 6e 79 6d 6f 75 73 27 2c  dy','anonymous',
0c50: 27 72 65 61 64 65 72 27 2c 27 64 65 76 65 6c 6f  'reader','develo
0c60: 70 65 72 27 29 22 0a 53 45 54 54 49 4e 47 20 20  per')".SETTING  
0c70: 3d 20 22 53 45 4c 45 43 54 20 76 61 6c 75 65 20  = "SELECT value 
0c80: 46 52 4f 4d 20 63 6f 6e 66 69 67 20 57 48 45 52  FROM config WHER
0c90: 45 20 6e 61 6d 65 3d 3f 31 22 0a 0a 23 20 57 68  E name=?1"..# Wh
0ca0: 61 74 20 61 72 72 69 76 65 73 2c 20 61 6e 64 20  at arrives, and 
0cb0: 77 68 61 74 20 69 73 20 61 6e 73 77 65 72 65 64  what is answered
0cc0: 2e 0a 73 74 72 75 63 74 20 49 64 65 6e 74 69 74  ..struct Identit
0cd0: 79 20 23 20 66 72 6f 6d 20 74 68 65 20 6c 61 75  y # from the lau
0ce0: 6e 63 68 65 72 2c 20 61 73 20 46 6f 73 73 69 6c  ncher, as Fossil
0cf0: 20 67 61 76 65 20 69 74 0a 20 20 69 6e 63 6c 75   gave it.  inclu
0d00: 64 65 20 4a 53 4f 4e 3a 3a 53 65 72 69 61 6c 69  de JSON::Seriali
0d10: 7a 61 62 6c 65 0a 20 20 67 65 74 74 65 72 20 75  zable.  getter u
0d20: 73 65 72 20 3a 20 53 74 72 69 6e 67 3f 0a 20 20  ser : String?.  
0d30: 67 65 74 74 65 72 20 63 61 70 61 62 69 6c 69 74  getter capabilit
0d40: 69 65 73 20 3a 20 53 74 72 69 6e 67 0a 20 20 67  ies : String.  g
0d50: 65 74 74 65 72 20 72 65 70 6f 73 69 74 6f 72 79  etter repository
0d60: 20 3a 20 53 74 72 69 6e 67 0a 65 6e 64 0a 0a 73   : String.end..s
0d70: 74 72 75 63 74 20 52 65 71 75 65 73 74 20 23 20  truct Request # 
0d80: 66 72 6f 6d 20 74 68 65 20 63 61 6c 6c 65 72 3b  from the caller;
0d90: 20 61 6e 79 20 6f 74 68 65 72 20 6b 65 79 20 69   any other key i
0da0: 73 20 72 65 66 75 73 65 64 0a 20 20 69 6e 63 6c  s refused.  incl
0db0: 75 64 65 20 4a 53 4f 4e 3a 3a 53 65 72 69 61 6c  ude JSON::Serial
0dc0: 69 7a 61 62 6c 65 0a 20 20 69 6e 63 6c 75 64 65  izable.  include
0dd0: 20 4a 53 4f 4e 3a 3a 53 65 72 69 61 6c 69 7a 61   JSON::Serializa
0de0: 62 6c 65 3a 3a 53 74 72 69 63 74 0a 20 20 67 65  ble::Strict.  ge
0df0: 74 74 65 72 20 6f 63 78 5f 72 65 63 65 69 70 74  tter ocx_receipt
0e00: 20 3a 20 49 6e 74 33 32 0a 20 20 67 65 74 74 65   : Int32.  gette
0e10: 72 20 61 72 74 69 66 61 63 74 73 20 3d 20 5b 5d  r artifacts = []
0e20: 20 6f 66 20 53 74 72 69 6e 67 0a 20 20 67 65 74   of String.  get
0e30: 74 65 72 20 63 61 70 61 62 69 6c 69 74 69 65 73  ter capabilities
0e40: 5f 6f 66 20 3a 20 53 74 72 69 6e 67 3f 0a 65 6e  _of : String?.en
0e50: 64 0a 0a 63 6c 61 73 73 20 41 72 74 69 66 61 63  d..class Artifac
0e60: 74 0a 20 20 69 6e 63 6c 75 64 65 20 4a 53 4f 4e  t.  include JSON
0e70: 3a 3a 53 65 72 69 61 6c 69 7a 61 62 6c 65 0a 20  ::Serializable. 
0e80: 20 70 72 6f 70 65 72 74 79 20 61 72 74 69 66 61   property artifa
0e90: 63 74 20 3a 20 53 74 72 69 6e 67 2c 20 72 65 63  ct : String, rec
0ea0: 65 69 76 65 64 20 3d 20 66 61 6c 73 65 2c 20 73  eived = false, s
0eb0: 65 6e 64 65 72 20 3a 20 53 74 72 69 6e 67 3f 20  ender : String? 
0ec0: 3d 20 6e 69 6c 2c 20 63 61 70 61 62 69 6c 69 74  = nil, capabilit
0ed0: 69 65 73 20 3a 20 53 74 72 69 6e 67 3f 20 3d 20  ies : String? = 
0ee0: 6e 69 6c 0a 20 20 70 72 6f 70 65 72 74 79 20 72  nil.  property r
0ef0: 65 63 65 69 70 74 20 3a 20 49 6e 74 36 34 3f 20  eceipt : Int64? 
0f00: 3d 20 6e 69 6c 2c 20 72 69 64 20 3a 20 49 6e 74  = nil, rid : Int
0f10: 36 34 3f 20 3d 20 6e 69 6c 0a 0a 20 20 64 65 66  64? = nil..  def
0f20: 20 69 6e 69 74 69 61 6c 69 7a 65 28 40 61 72 74   initialize(@art
0f30: 69 66 61 63 74 29 3b 20 65 6e 64 0a 65 6e 64 0a  ifact); end.end.
0f40: 0a 63 6c 61 73 73 20 55 73 65 72 0a 20 20 69 6e  .class User.  in
0f50: 63 6c 75 64 65 20 4a 53 4f 4e 3a 3a 53 65 72 69  clude JSON::Seri
0f60: 61 6c 69 7a 61 62 6c 65 0a 20 20 70 72 6f 70 65  alizable.  prope
0f70: 72 74 79 20 75 73 65 72 20 3a 20 53 74 72 69 6e  rty user : Strin
0f80: 67 0a 20 20 40 5b 4a 53 4f 4e 3a 3a 46 69 65 6c  g.  @[JSON::Fiel
0f90: 64 28 65 6d 69 74 5f 6e 75 6c 6c 3a 20 74 72 75  d(emit_null: tru
0fa0: 65 29 5d 0a 20 20 70 72 6f 70 65 72 74 79 20 63  e)].  property c
0fb0: 61 70 61 62 69 6c 69 74 69 65 73 20 3a 20 53 74  apabilities : St
0fc0: 72 69 6e 67 3f 20 3d 20 6e 69 6c 0a 20 20 70 72  ring? = nil.  pr
0fd0: 6f 70 65 72 74 79 20 77 69 6b 69 5f 6d 6f 64 65  operty wiki_mode
0fe0: 72 61 74 65 64 20 3a 20 42 6f 6f 6c 3f 20 3d 20  rated : Bool? = 
0ff0: 6e 69 6c 2c 20 74 69 63 6b 65 74 5f 6d 6f 64 65  nil, ticket_mode
1000: 72 61 74 65 64 20 3a 20 42 6f 6f 6c 3f 20 3d 20  rated : Bool? = 
1010: 6e 69 6c 0a 0a 20 20 64 65 66 20 69 6e 69 74 69  nil..  def initi
1020: 61 6c 69 7a 65 28 40 75 73 65 72 29 3b 20 65 6e  alize(@user); en
1030: 64 0a 65 6e 64 0a 0a 72 65 63 6f 72 64 20 41 6e  d.end..record An
1040: 73 77 65 72 2c 20 61 72 74 69 66 61 63 74 73 20  swer, artifacts 
1050: 3a 20 41 72 72 61 79 28 41 72 74 69 66 61 63 74  : Array(Artifact
1060: 29 2c 20 75 73 65 72 20 3a 20 55 73 65 72 3f 20  ), user : User? 
1070: 7b 20 69 6e 63 6c 75 64 65 20 4a 53 4f 4e 3a 3a  { include JSON::
1080: 53 65 72 69 61 6c 69 7a 61 62 6c 65 20 7d 0a 0a  Serializable }..
1090: 63 6c 61 73 73 20 52 65 66 75 73 65 64 20 3c 20  class Refused < 
10a0: 45 78 63 65 70 74 69 6f 6e 3b 20 65 6e 64 20 23  Exception; end #
10b0: 20 61 20 62 61 64 20 72 65 71 75 65 73 74 0a 0a   a bad request..
10c0: 63 6c 61 73 73 20 4c 6f 63 6b 65 64 20 3c 20 45  class Locked < E
10d0: 78 63 65 70 74 69 6f 6e 3b 20 65 6e 64 20 23 20  xception; end # 
10e0: 46 6f 73 73 69 6c 20 69 73 20 77 72 69 74 69 6e  Fossil is writin
10f0: 67 3a 20 77 61 69 74 20 61 6e 64 20 74 72 79 20  g: wait and try 
1100: 61 67 61 69 6e 0a 0a 23 20 41 20 75 73 65 72 27  again..# A user'
1110: 73 20 63 61 70 61 62 69 6c 69 74 69 65 73 20 61  s capabilities a
1120: 73 20 46 6f 73 73 69 6c 20 65 78 70 61 6e 64 73  s Fossil expands
1130: 20 74 68 65 6d 3a 20 74 68 65 69 72 20 6f 77 6e   them: their own
1140: 2c 20 61 6e 6f 6e 79 6d 6f 75 73 27 73 20 61 6e  , anonymous's an
1150: 64 20 6e 6f 62 6f 64 79 27 73 2c 20 74 68 65 6e  d nobody's, then
1160: 20 61 64 6d 69 6e 27 73 2c 20 64 65 76 65 6c 6f   admin's, develo
1170: 70 65 72 27 73 20 61 6e 64 20 72 65 61 64 65 72  per's and reader
1180: 27 73 2e 0a 64 65 66 20 65 78 70 61 6e 64 65 64  's..def expanded
1190: 28 63 61 70 20 3a 20 53 74 72 69 6e 67 2c 20 73  (cap : String, s
11a0: 70 65 63 69 61 6c 20 3a 20 48 61 73 68 28 53 74  pecial : Hash(St
11b0: 72 69 6e 67 2c 20 53 74 72 69 6e 67 29 29 20 3a  ring, String)) :
11c0: 20 53 74 72 69 6e 67 0a 20 20 63 61 70 20 2b 3d   String.  cap +=
11d0: 20 73 70 65 63 69 61 6c 5b 22 61 6e 6f 6e 79 6d   special["anonym
11e0: 6f 75 73 22 5d 3f 2e 74 6f 5f 73 20 2b 20 73 70  ous"]?.to_s + sp
11f0: 65 63 69 61 6c 5b 22 6e 6f 62 6f 64 79 22 5d 3f  ecial["nobody"]?
1200: 2e 74 6f 5f 73 0a 20 20 63 61 70 20 2b 3d 20 22  .to_s.  cap += "
1210: 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71  bcdefghijklmnopq
1220: 72 74 77 7a 32 33 34 35 36 37 41 44 22 20 69 66  rtwz234567AD" if
1230: 20 63 61 70 2e 69 6e 63 6c 75 64 65 73 3f 28 27   cap.includes?('
1240: 61 27 29 20 7c 7c 20 63 61 70 2e 69 6e 63 6c 75  a') || cap.inclu
1250: 64 65 73 3f 28 27 73 27 29 0a 20 20 63 61 70 20  des?('s').  cap 
1260: 2b 3d 20 73 70 65 63 69 61 6c 5b 22 64 65 76 65  += special["deve
1270: 6c 6f 70 65 72 22 5d 3f 2e 74 6f 5f 73 20 69 66  loper"]?.to_s if
1280: 20 63 61 70 2e 69 6e 63 6c 75 64 65 73 3f 28 27   cap.includes?('
1290: 76 27 29 0a 20 20 63 61 70 20 2b 3d 20 73 70 65  v').  cap += spe
12a0: 63 69 61 6c 5b 22 72 65 61 64 65 72 22 5d 3f 2e  cial["reader"]?.
12b0: 74 6f 5f 73 20 69 66 20 63 61 70 2e 69 6e 63 6c  to_s if cap.incl
12c0: 75 64 65 73 3f 28 27 75 27 29 0a 20 20 63 61 70  udes?('u').  cap
12d0: 20 2b 3d 20 73 70 65 63 69 61 6c 5b 22 64 65 76   += special["dev
12e0: 65 6c 6f 70 65 72 22 5d 3f 2e 74 6f 5f 73 20 69  eloper"]?.to_s i
12f0: 66 20 63 61 70 2e 69 6e 63 6c 75 64 65 73 3f 28  f cap.includes?(
1300: 27 76 27 29 0a 20 20 28 22 61 22 2e 2e 22 7a 22  'v').  ("a".."z"
1310: 29 2e 74 6f 5f 61 2e 63 6f 6e 63 61 74 28 28 22  ).to_a.concat(("
1320: 30 22 2e 2e 22 39 22 29 2e 74 6f 5f 61 29 2e 63  0".."9").to_a).c
1330: 6f 6e 63 61 74 28 28 22 41 22 2e 2e 22 5a 22 29  oncat(("A".."Z")
1340: 2e 74 6f 5f 61 29 2e 73 65 6c 65 63 74 20 7b 20  .to_a).select { 
1350: 7c 63 7c 20 63 61 70 2e 69 6e 63 6c 75 64 65 73  |c| cap.includes
1360: 3f 28 63 29 20 7d 2e 6a 6f 69 6e 0a 65 6e 64 0a  ?(c) }.join.end.
1370: 0a 23 20 53 74 6f 70 73 20 6f 6e 20 61 6e 20 53  .# Stops on an S
1380: 51 4c 69 74 65 20 65 72 72 6f 72 3b 20 61 20 6c  QLite error; a l
1390: 6f 63 6b 20 69 73 20 72 65 70 6f 72 74 65 64 20  ock is reported 
13a0: 61 70 61 72 74 2c 20 73 6f 20 74 68 65 20 63 61  apart, so the ca
13b0: 6c 6c 65 72 20 63 61 6e 20 77 61 69 74 2e 0a 64  ller can wait..d
13c0: 65 66 20 73 71 6c 69 74 65 28 64 62 20 3a 20 56  ef sqlite(db : V
13d0: 6f 69 64 2a 2c 20 63 6f 64 65 20 3a 20 49 6e 74  oid*, code : Int
13e0: 33 32 29 20 3a 20 4e 69 6c 0a 20 20 72 65 74 75  32) : Nil.  retu
13f0: 72 6e 20 69 66 20 63 6f 64 65 20 3d 3d 20 30 0a  rn if code == 0.
1400: 20 20 72 61 69 73 65 20 4c 6f 63 6b 65 64 2e 6e    raise Locked.n
1410: 65 77 20 69 66 20 63 6f 64 65 20 3d 3d 20 35 20  ew if code == 5 
1420: 7c 7c 20 63 6f 64 65 20 3d 3d 20 36 20 23 20 42  || code == 6 # B
1430: 55 53 59 2c 20 4c 4f 43 4b 45 44 0a 20 20 72 61  USY, LOCKED.  ra
1440: 69 73 65 20 49 4f 3a 3a 45 72 72 6f 72 2e 6e 65  ise IO::Error.ne
1450: 77 28 22 73 71 6c 69 74 65 20 65 72 72 6f 72 20  w("sqlite error 
1460: 23 7b 63 6f 64 65 7d 3a 20 23 7b 53 74 72 69 6e  #{code}: #{Strin
1470: 67 2e 6e 65 77 28 4c 69 62 53 51 4c 69 74 65 33  g.new(LibSQLite3
1480: 2e 65 72 72 6d 73 67 28 64 62 29 29 7d 22 29 0a  .errmsg(db))}").
1490: 65 6e 64 0a 0a 23 20 52 75 6e 73 20 6f 6e 65 20  end..# Runs one 
14a0: 66 69 78 65 64 20 71 75 65 72 79 20 61 6e 64 20  fixed query and 
14b0: 72 65 74 75 72 6e 73 20 69 74 73 20 72 6f 77 73  returns its rows
14c0: 2e 0a 64 65 66 20 72 6f 77 73 28 64 62 20 3a 20  ..def rows(db : 
14d0: 56 6f 69 64 2a 2c 20 73 71 6c 20 3a 20 53 74 72  Void*, sql : Str
14e0: 69 6e 67 2c 20 76 61 6c 75 65 20 3a 20 53 74 72  ing, value : Str
14f0: 69 6e 67 3f 20 3d 20 6e 69 6c 29 20 3a 20 41 72  ing? = nil) : Ar
1500: 72 61 79 28 41 72 72 61 79 28 53 74 72 69 6e 67  ray(Array(String
1510: 3f 29 29 0a 20 20 73 74 61 74 65 6d 65 6e 74 20  ?)).  statement 
1520: 3d 20 50 6f 69 6e 74 65 72 28 56 6f 69 64 29 2e  = Pointer(Void).
1530: 6e 75 6c 6c 0a 20 20 62 65 67 69 6e 0a 20 20 20  null.  begin.   
1540: 20 73 71 6c 69 74 65 28 64 62 2c 20 4c 69 62 53   sqlite(db, LibS
1550: 51 4c 69 74 65 33 2e 70 72 65 70 61 72 65 5f 76  QLite3.prepare_v
1560: 32 28 64 62 2c 20 73 71 6c 2c 20 2d 31 2c 20 70  2(db, sql, -1, p
1570: 6f 69 6e 74 65 72 6f 66 28 73 74 61 74 65 6d 65  ointerof(stateme
1580: 6e 74 29 2c 20 6e 69 6c 29 29 0a 20 20 20 20 73  nt), nil)).    s
1590: 71 6c 69 74 65 28 64 62 2c 20 4c 69 62 53 51 4c  qlite(db, LibSQL
15a0: 69 74 65 33 2e 62 69 6e 64 5f 74 65 78 74 28 73  ite3.bind_text(s
15b0: 74 61 74 65 6d 65 6e 74 2c 20 31 2c 20 76 61 6c  tatement, 1, val
15c0: 75 65 2c 20 76 61 6c 75 65 2e 62 79 74 65 73 69  ue, value.bytesi
15d0: 7a 65 2c 20 50 6f 69 6e 74 65 72 28 56 6f 69 64  ze, Pointer(Void
15e0: 29 2e 6e 65 77 28 55 49 6e 74 36 34 3a 3a 4d 41  ).new(UInt64::MA
15f0: 58 29 29 29 20 69 66 20 76 61 6c 75 65 0a 20 20  X))) if value.  
1600: 20 20 72 65 73 75 6c 74 20 3d 20 5b 5d 20 6f 66    result = [] of
1610: 20 41 72 72 61 79 28 53 74 72 69 6e 67 3f 29 0a   Array(String?).
1620: 20 20 20 20 77 68 69 6c 65 20 28 63 6f 64 65 20      while (code 
1630: 3d 20 4c 69 62 53 51 4c 69 74 65 33 2e 73 74 65  = LibSQLite3.ste
1640: 70 28 73 74 61 74 65 6d 65 6e 74 29 29 20 3d 3d  p(statement)) ==
1650: 20 31 30 30 20 23 20 61 20 72 6f 77 0a 20 20 20   100 # a row.   
1660: 20 20 20 72 65 73 75 6c 74 20 3c 3c 20 41 72 72     result << Arr
1670: 61 79 2e 6e 65 77 28 4c 69 62 53 51 4c 69 74 65  ay.new(LibSQLite
1680: 33 2e 63 6f 6c 75 6d 6e 5f 63 6f 75 6e 74 28 73  3.column_count(s
1690: 74 61 74 65 6d 65 6e 74 29 29 20 7b 20 7c 69 7c  tatement)) { |i|
16a0: 20 74 65 78 74 28 73 74 61 74 65 6d 65 6e 74 2c   text(statement,
16b0: 20 69 29 20 7d 0a 20 20 20 20 65 6e 64 0a 20 20   i) }.    end.  
16c0: 20 20 73 71 6c 69 74 65 28 64 62 2c 20 63 6f 64    sqlite(db, cod
16d0: 65 29 20 75 6e 6c 65 73 73 20 63 6f 64 65 20 3d  e) unless code =
16e0: 3d 20 31 30 31 20 23 20 64 6f 6e 65 0a 20 20 20  = 101 # done.   
16f0: 20 72 65 73 75 6c 74 0a 20 20 65 6e 73 75 72 65   result.  ensure
1700: 0a 20 20 20 20 4c 69 62 53 51 4c 69 74 65 33 2e  .    LibSQLite3.
1710: 66 69 6e 61 6c 69 7a 65 28 73 74 61 74 65 6d 65  finalize(stateme
1720: 6e 74 29 0a 20 20 65 6e 64 0a 65 6e 64 0a 0a 64  nt).  end.end..d
1730: 65 66 20 74 65 78 74 28 73 74 61 74 65 6d 65 6e  ef text(statemen
1740: 74 20 3a 20 56 6f 69 64 2a 2c 20 63 6f 6c 75 6d  t : Void*, colum
1750: 6e 20 3a 20 49 6e 74 33 32 29 20 3a 20 53 74 72  n : Int32) : Str
1760: 69 6e 67 3f 0a 20 20 74 20 3d 20 4c 69 62 53 51  ing?.  t = LibSQ
1770: 4c 69 74 65 33 2e 63 6f 6c 75 6d 6e 5f 74 65 78  Lite3.column_tex
1780: 74 28 73 74 61 74 65 6d 65 6e 74 2c 20 63 6f 6c  t(statement, col
1790: 75 6d 6e 29 0a 20 20 74 2e 6e 75 6c 6c 3f 20 3f  umn).  t.null? ?
17a0: 20 6e 69 6c 20 3a 20 53 74 72 69 6e 67 2e 6e 65   nil : String.ne
17b0: 77 28 74 29 0a 65 6e 64 0a 0a 23 20 4f 6e 65 20  w(t).end..# One 
17c0: 72 65 61 64 2d 6f 6e 6c 79 20 73 6e 61 70 73 68  read-only snapsh
17d0: 6f 74 20 6f 66 20 77 68 61 74 20 74 68 65 20 72  ot of what the r
17e0: 65 70 6f 73 69 74 6f 72 79 20 73 61 79 73 2e 20  epository says. 
17f0: 53 51 4c 69 74 65 27 73 20 6f 77 6e 20 6c 6f 63  SQLite's own loc
1800: 6b 69 6e 67 20 61 70 70 6c 69 65 73 3b 20 69 74  king applies; it
1810: 20 6e 65 76 65 72 20 77 61 69 74 73 20 69 74 73   never waits its
1820: 65 6c 66 2e 0a 64 65 66 20 72 65 61 64 28 70 61  elf..def read(pa
1830: 74 68 20 3a 20 53 74 72 69 6e 67 2c 20 63 61 70  th : String, cap
1840: 73 20 3a 20 53 74 72 69 6e 67 2c 20 72 65 71 75  s : String, requ
1850: 65 73 74 20 3a 20 52 65 71 75 65 73 74 29 20 3a  est : Request) :
1860: 20 41 6e 73 77 65 72 0a 20 20 64 62 20 3d 20 50   Answer.  db = P
1870: 6f 69 6e 74 65 72 28 56 6f 69 64 29 2e 6e 75 6c  ointer(Void).nul
1880: 6c 0a 20 20 62 65 67 69 6e 0a 20 20 20 20 73 71  l.  begin.    sq
1890: 6c 69 74 65 28 64 62 2c 20 4c 69 62 53 51 4c 69  lite(db, LibSQLi
18a0: 74 65 33 2e 6f 70 65 6e 5f 76 32 28 22 66 69 6c  te3.open_v2("fil
18b0: 65 3a 23 7b 55 52 49 2e 65 6e 63 6f 64 65 5f 70  e:#{URI.encode_p
18c0: 61 74 68 28 70 61 74 68 29 7d 3f 6d 6f 64 65 3d  ath(path)}?mode=
18d0: 72 6f 22 2c 20 70 6f 69 6e 74 65 72 6f 66 28 64  ro", pointerof(d
18e0: 62 29 2c 20 30 78 30 31 20 7c 20 30 78 34 30 2c  b), 0x01 | 0x40,
18f0: 20 6e 69 6c 29 29 20 23 20 72 65 61 64 2d 6f 6e   nil)) # read-on
1900: 6c 79 2c 20 55 52 49 0a 20 20 20 20 73 6e 61 70  ly, URI.    snap
1910: 73 68 6f 74 28 64 62 2c 20 63 61 70 73 2c 20 72  shot(db, caps, r
1920: 65 71 75 65 73 74 29 0a 20 20 65 6e 73 75 72 65  equest).  ensure
1930: 0a 20 20 20 20 4c 69 62 53 51 4c 69 74 65 33 2e  .    LibSQLite3.
1940: 63 6c 6f 73 65 5f 76 32 28 64 62 29 0a 20 20 65  close_v2(db).  e
1950: 6e 64 0a 65 6e 64 0a 0a 64 65 66 20 73 6e 61 70  nd.end..def snap
1960: 73 68 6f 74 28 64 62 20 3a 20 56 6f 69 64 2a 2c  shot(db : Void*,
1970: 20 63 61 70 73 20 3a 20 53 74 72 69 6e 67 2c 20   caps : String, 
1980: 72 65 71 75 65 73 74 20 3a 20 52 65 71 75 65 73  request : Reques
1990: 74 29 20 3a 20 41 6e 73 77 65 72 0a 20 20 73 71  t) : Answer.  sq
19a0: 6c 69 74 65 28 64 62 2c 20 4c 69 62 53 51 4c 69  lite(db, LibSQLi
19b0: 74 65 33 2e 62 75 73 79 5f 74 69 6d 65 6f 75 74  te3.busy_timeout
19c0: 28 64 62 2c 20 30 29 29 0a 20 20 72 6f 77 73 28  (db, 0)).  rows(
19d0: 64 62 2c 20 22 50 52 41 47 4d 41 20 71 75 65 72  db, "PRAGMA quer
19e0: 79 5f 6f 6e 6c 79 3d 31 22 29 0a 20 20 72 6f 77  y_only=1").  row
19f0: 73 28 64 62 2c 20 22 42 45 47 49 4e 22 29 0a 20  s(db, "BEGIN"). 
1a00: 20 73 70 65 63 69 61 6c 20 3d 20 72 6f 77 73 28   special = rows(
1a10: 64 62 2c 20 44 45 46 41 55 4c 54 53 29 2e 74 6f  db, DEFAULTS).to
1a20: 5f 68 20 7b 20 7c 72 7c 20 7b 72 5b 30 5d 2e 74  _h { |r| {r[0].t
1a30: 6f 5f 73 2c 20 72 5b 31 5d 2e 74 6f 5f 73 7d 20  o_s, r[1].to_s} 
1a40: 7d 0a 20 20 61 72 74 69 66 61 63 74 73 20 3d 20  }.  artifacts = 
1a50: 72 65 71 75 65 73 74 2e 61 72 74 69 66 61 63 74  request.artifact
1a60: 73 2e 6d 61 70 20 64 6f 20 7c 68 61 73 68 7c 0a  s.map do |hash|.
1a70: 20 20 20 20 66 6f 75 6e 64 20 3d 20 41 72 74 69      found = Arti
1a80: 66 61 63 74 2e 6e 65 77 28 68 61 73 68 29 0a 20  fact.new(hash). 
1a90: 20 20 20 69 66 20 28 72 20 3d 20 72 6f 77 73 28     if (r = rows(
1aa0: 64 62 2c 20 41 52 54 49 46 41 43 54 2c 20 68 61  db, ARTIFACT, ha
1ab0: 73 68 29 2e 66 69 72 73 74 3f 29 20 26 26 20 28  sh).first?) && (
1ac0: 72 5b 32 5d 20 3d 3d 20 22 30 22 20 7c 7c 20 63  r[2] == "0" || c
1ad0: 61 70 73 2e 69 6e 63 6c 75 64 65 73 3f 28 27 78  aps.includes?('x
1ae0: 27 29 29 20 23 20 70 72 69 76 61 74 65 20 6f 6e  ')) # private on
1af0: 6c 79 20 66 6f 72 20 78 0a 20 20 20 20 20 20 66  ly for x.      f
1b00: 6f 75 6e 64 2e 72 65 63 65 69 76 65 64 2c 20 66  ound.received, f
1b10: 6f 75 6e 64 2e 72 69 64 2c 20 66 6f 75 6e 64 2e  ound.rid, found.
1b20: 72 65 63 65 69 70 74 20 3d 20 74 72 75 65 2c 20  receipt = true, 
1b30: 72 5b 30 5d 2e 74 6f 5f 73 2e 74 6f 5f 69 36 34  r[0].to_s.to_i64
1b40: 2c 20 72 5b 31 5d 2e 74 6f 5f 73 2e 74 6f 5f 69  , r[1].to_s.to_i
1b50: 36 34 0a 20 20 20 20 20 20 66 6f 75 6e 64 2e 73  64.      found.s
1b60: 65 6e 64 65 72 2c 20 66 6f 75 6e 64 2e 63 61 70  ender, found.cap
1b70: 61 62 69 6c 69 74 69 65 73 20 3d 20 72 5b 33 5d  abilities = r[3]
1b80: 2c 20 65 78 70 61 6e 64 65 64 28 72 5b 34 5d 2e  , expanded(r[4].
1b90: 74 6f 5f 73 2c 20 73 70 65 63 69 61 6c 29 20 69  to_s, special) i
1ba0: 66 20 72 5b 33 5d 0a 20 20 20 20 65 6e 64 0a 20  f r[3].    end. 
1bb0: 20 20 20 66 6f 75 6e 64 0a 20 20 65 6e 64 0a 20     found.  end. 
1bc0: 20 75 73 65 72 20 3d 20 72 65 71 75 65 73 74 2e   user = request.
1bd0: 63 61 70 61 62 69 6c 69 74 69 65 73 5f 6f 66 2e  capabilities_of.
1be0: 74 72 79 20 64 6f 20 7c 6e 61 6d 65 7c 0a 20 20  try do |name|.  
1bf0: 20 20 68 65 6c 64 20 3d 20 55 73 65 72 2e 6e 65    held = User.ne
1c00: 77 28 6e 61 6d 65 29 0a 20 20 20 20 69 66 20 72  w(name).    if r
1c10: 20 3d 20 72 6f 77 73 28 64 62 2c 20 55 53 45 52   = rows(db, USER
1c20: 2c 20 6e 61 6d 65 29 2e 66 69 72 73 74 3f 0a 20  , name).first?. 
1c30: 20 20 20 20 20 68 65 6c 64 2e 63 61 70 61 62 69       held.capabi
1c40: 6c 69 74 69 65 73 20 3d 20 65 78 70 61 6e 64 65  lities = expande
1c50: 64 28 72 5b 30 5d 2e 74 6f 5f 73 2c 20 73 70 65  d(r[0].to_s, spe
1c60: 63 69 61 6c 29 0a 20 20 20 20 20 20 6f 6e 20 3d  cial).      on =
1c70: 20 2d 3e 28 73 65 74 74 69 6e 67 20 3a 20 53 74   ->(setting : St
1c80: 72 69 6e 67 29 20 7b 20 7b 22 6f 6e 22 2c 20 22  ring) { {"on", "
1c90: 79 65 73 22 2c 20 22 74 72 75 65 22 2c 20 22 31  yes", "true", "1
1ca0: 22 7d 2e 69 6e 63 6c 75 64 65 73 3f 28 72 6f 77  "}.includes?(row
1cb0: 73 28 64 62 2c 20 53 45 54 54 49 4e 47 2c 20 73  s(db, SETTING, s
1cc0: 65 74 74 69 6e 67 29 2e 66 69 72 73 74 3f 2e 74  etting).first?.t
1cd0: 72 79 28 26 2e 66 69 72 73 74 29 2e 74 6f 5f 73  ry(&.first).to_s
1ce0: 2e 64 6f 77 6e 63 61 73 65 29 20 7d 0a 20 20 20  .downcase) }.   
1cf0: 20 20 20 68 65 6c 64 2e 77 69 6b 69 5f 6d 6f 64     held.wiki_mod
1d00: 65 72 61 74 65 64 20 3d 20 6f 6e 2e 63 61 6c 6c  erated = on.call
1d10: 28 22 6d 6f 64 72 65 71 2d 77 69 6b 69 22 29 20  ("modreq-wiki") 
1d20: 26 26 20 21 68 65 6c 64 2e 63 61 70 61 62 69 6c  && !held.capabil
1d30: 69 74 69 65 73 2e 74 6f 5f 73 2e 69 6e 63 6c 75  ities.to_s.inclu
1d40: 64 65 73 3f 28 27 6c 27 29 0a 20 20 20 20 20 20  des?('l').      
1d50: 68 65 6c 64 2e 74 69 63 6b 65 74 5f 6d 6f 64 65  held.ticket_mode
1d60: 72 61 74 65 64 20 3d 20 6f 6e 2e 63 61 6c 6c 28  rated = on.call(
1d70: 22 6d 6f 64 72 65 71 2d 74 6b 74 22 29 20 26 26  "modreq-tkt") &&
1d80: 20 21 68 65 6c 64 2e 63 61 70 61 62 69 6c 69 74   !held.capabilit
1d90: 69 65 73 2e 74 6f 5f 73 2e 69 6e 63 6c 75 64 65  ies.to_s.include
1da0: 73 3f 28 27 71 27 29 0a 20 20 20 20 65 6e 64 0a  s?('q').    end.
1db0: 20 20 20 20 68 65 6c 64 0a 20 20 65 6e 64 0a 20      held.  end. 
1dc0: 20 72 6f 77 73 28 64 62 2c 20 22 43 4f 4d 4d 49   rows(db, "COMMI
1dd0: 54 22 29 0a 20 20 41 6e 73 77 65 72 2e 6e 65 77  T").  Answer.new
1de0: 28 61 72 74 69 66 61 63 74 73 2c 20 75 73 65 72  (artifacts, user
1df0: 29 0a 65 6e 64 0a 0a 23 20 52 65 61 64 73 20 6f  ).end..# Reads o
1e00: 6e 65 20 6c 69 6e 65 20 6f 66 20 61 74 20 6d 6f  ne line of at mo
1e10: 73 74 20 60 6c 69 6d 69 74 60 20 62 79 74 65 73  st `limit` bytes
1e20: 20 62 65 66 6f 72 65 20 60 64 65 61 64 6c 69 6e   before `deadlin
1e30: 65 60 2e 0a 64 65 66 20 6c 69 6e 65 28 73 6f 63  e`..def line(soc
1e40: 6b 65 74 20 3a 20 55 4e 49 58 53 6f 63 6b 65 74  ket : UNIXSocket
1e50: 2c 20 6c 69 6d 69 74 20 3a 20 49 6e 74 33 32 2c  , limit : Int32,
1e60: 20 64 65 61 64 6c 69 6e 65 20 3a 20 54 69 6d 65   deadline : Time
1e70: 29 20 3a 20 53 74 72 69 6e 67 0a 20 20 74 65 78  ) : String.  tex
1e80: 74 20 3d 20 49 4f 3a 3a 4d 65 6d 6f 72 79 2e 6e  t = IO::Memory.n
1e90: 65 77 0a 20 20 6c 6f 6f 70 20 64 6f 0a 20 20 20  ew.  loop do.   
1ea0: 20 73 6f 63 6b 65 74 2e 72 65 61 64 5f 74 69 6d   socket.read_tim
1eb0: 65 6f 75 74 20 3d 20 7b 64 65 61 64 6c 69 6e 65  eout = {deadline
1ec0: 20 2d 20 54 69 6d 65 2e 75 74 63 2c 20 31 2e 6d   - Time.utc, 1.m
1ed0: 69 6c 6c 69 73 65 63 6f 6e 64 7d 2e 6d 61 78 0a  illisecond}.max.
1ee0: 20 20 20 20 62 79 74 65 20 3d 20 73 6f 63 6b 65      byte = socke
1ef0: 74 2e 72 65 61 64 5f 62 79 74 65 20 7c 7c 20 72  t.read_byte || r
1f00: 61 69 73 65 20 52 65 66 75 73 65 64 2e 6e 65 77  aise Refused.new
1f10: 28 22 65 78 70 65 63 74 65 64 20 74 77 6f 20 4a  ("expected two J
1f20: 53 4f 4e 20 6c 69 6e 65 73 22 29 0a 20 20 20 20  SON lines").    
1f30: 72 65 74 75 72 6e 20 74 65 78 74 2e 74 6f 5f 73  return text.to_s
1f40: 20 69 66 20 62 79 74 65 20 3d 3d 20 31 30 0a 20   if byte == 10. 
1f50: 20 20 20 72 61 69 73 65 20 52 65 66 75 73 65 64     raise Refused
1f60: 2e 6e 65 77 28 22 6c 69 6e 65 20 74 6f 6f 20 6c  .new("line too l
1f70: 6f 6e 67 22 29 20 69 66 20 74 65 78 74 2e 73 69  ong") if text.si
1f80: 7a 65 20 3e 3d 20 6c 69 6d 69 74 0a 20 20 20 20  ze >= limit.    
1f90: 74 65 78 74 2e 77 72 69 74 65 5f 62 79 74 65 28  text.write_byte(
1fa0: 62 79 74 65 29 0a 20 20 65 6e 64 0a 72 65 73 63  byte).  end.resc
1fb0: 75 65 20 49 4f 3a 3a 54 69 6d 65 6f 75 74 45 72  ue IO::TimeoutEr
1fc0: 72 6f 72 0a 20 20 72 61 69 73 65 20 52 65 66 75  ror.  raise Refu
1fd0: 73 65 64 2e 6e 65 77 28 22 72 65 71 75 65 73 74  sed.new("request
1fe0: 20 6e 6f 74 20 72 65 63 65 69 76 65 64 20 77 69   not received wi
1ff0: 74 68 69 6e 20 23 7b 52 45 41 44 5f 57 49 54 48  thin #{READ_WITH
2000: 49 4e 2e 74 6f 74 61 6c 5f 73 65 63 6f 6e 64 73  IN.total_seconds
2010: 2e 74 6f 5f 69 7d 20 73 22 29 0a 65 6e 64 0a 0a  .to_i} s").end..
2020: 23 20 41 6e 73 77 65 72 73 20 6f 6e 65 20 63 6f  # Answers one co
2030: 6e 6e 65 63 74 69 6f 6e 2c 20 74 68 65 6e 20 63  nnection, then c
2040: 6c 6f 73 65 73 20 69 74 20 61 6e 64 20 6c 6f 67  loses it and log
2050: 73 20 6f 6e 65 20 6c 69 6e 65 2e 0a 64 65 66 20  s one line..def 
2060: 73 65 72 76 65 28 73 6f 63 6b 65 74 20 3a 20 55  serve(socket : U
2070: 4e 49 58 53 6f 63 6b 65 74 2c 20 73 74 61 72 74  NIXSocket, start
2080: 65 64 20 3a 20 54 69 6d 65 29 20 3a 20 4e 69 6c  ed : Time) : Nil
2090: 0a 20 20 6e 61 6d 65 2c 20 6f 75 74 63 6f 6d 65  .  name, outcome
20a0: 20 3d 20 22 2d 22 2c 20 22 6f 6b 22 0a 20 20 61   = "-", "ok".  a
20b0: 6e 73 77 65 72 20 3d 20 62 65 67 69 6e 0a 20 20  nswer = begin.  
20c0: 20 20 63 72 65 64 73 20 3d 20 53 74 61 74 69 63    creds = Static
20d0: 41 72 72 61 79 28 55 49 6e 74 33 32 2c 20 33 29  Array(UInt32, 3)
20e0: 2e 6e 65 77 28 30 29 20 23 20 53 4f 5f 50 45 45  .new(0) # SO_PEE
20f0: 52 43 52 45 44 3a 20 70 69 64 2c 20 75 69 64 2c  RCRED: pid, uid,
2100: 20 67 69 64 0a 20 20 20 20 73 69 7a 65 20 3d 20   gid.    size = 
2110: 73 69 7a 65 6f 66 28 74 79 70 65 6f 66 28 63 72  sizeof(typeof(cr
2120: 65 64 73 29 29 2e 74 6f 5f 75 33 32 0a 20 20 20  eds)).to_u32.   
2130: 20 72 61 69 73 65 20 52 65 66 75 73 65 64 2e 6e   raise Refused.n
2140: 65 77 28 22 70 65 65 72 20 75 69 64 20 64 69 66  ew("peer uid dif
2150: 66 65 72 73 22 29 20 75 6e 6c 65 73 73 20 4c 69  fers") unless Li
2160: 62 43 2e 67 65 74 73 6f 63 6b 6f 70 74 28 73 6f  bC.getsockopt(so
2170: 63 6b 65 74 2e 66 64 2c 20 31 2c 20 31 37 2c 20  cket.fd, 1, 17, 
2180: 70 6f 69 6e 74 65 72 6f 66 28 63 72 65 64 73 29  pointerof(creds)
2190: 2c 20 70 6f 69 6e 74 65 72 6f 66 28 73 69 7a 65  , pointerof(size
21a0: 29 29 20 3d 3d 20 30 20 26 26 20 63 72 65 64 73  )) == 0 && creds
21b0: 5b 31 5d 20 3d 3d 20 4c 69 62 43 2e 67 65 74 75  [1] == LibC.getu
21c0: 69 64 0a 20 20 20 20 69 64 65 6e 74 69 74 79 20  id.    identity 
21d0: 3d 20 49 64 65 6e 74 69 74 79 2e 66 72 6f 6d 5f  = Identity.from_
21e0: 6a 73 6f 6e 28 6c 69 6e 65 28 73 6f 63 6b 65 74  json(line(socket
21f0: 2c 20 31 30 32 34 2c 20 73 74 61 72 74 65 64 20  , 1024, started 
2200: 2b 20 52 45 41 44 5f 57 49 54 48 49 4e 29 29 0a  + READ_WITHIN)).
2210: 20 20 20 20 72 65 71 75 65 73 74 20 3d 20 52 65      request = Re
2220: 71 75 65 73 74 2e 66 72 6f 6d 5f 6a 73 6f 6e 28  quest.from_json(
2230: 6c 69 6e 65 28 73 6f 63 6b 65 74 2c 20 38 31 39  line(socket, 819
2240: 32 2c 20 73 74 61 72 74 65 64 20 2b 20 52 45 41  2, started + REA
2250: 44 5f 57 49 54 48 49 4e 29 29 0a 20 20 20 20 6e  D_WITHIN)).    n
2260: 61 6d 65 20 3d 20 69 64 65 6e 74 69 74 79 2e 72  ame = identity.r
2270: 65 70 6f 73 69 74 6f 72 79 0a 20 20 20 20 70 61  epository.    pa
2280: 74 68 20 3d 20 46 69 6c 65 2e 6a 6f 69 6e 28 52  th = File.join(R
2290: 45 50 4f 53 49 54 4f 52 49 45 53 2c 20 6e 61 6d  EPOSITORIES, nam
22a0: 65 20 2b 20 22 2e 66 6f 73 73 69 6c 22 29 0a 20  e + ".fossil"). 
22b0: 20 20 20 72 61 69 73 65 20 52 65 66 75 73 65 64     raise Refused
22c0: 2e 6e 65 77 28 22 69 6e 76 61 6c 69 64 20 72 65  .new("invalid re
22d0: 70 6f 73 69 74 6f 72 79 22 29 20 75 6e 6c 65 73  pository") unles
22e0: 73 20 6e 61 6d 65 2e 6d 61 74 63 68 65 73 3f 28  s name.matches?(
22f0: 2f 5c 41 5b 41 2d 5a 61 2d 7a 30 2d 39 5f 5d 5b  /\A[A-Za-z0-9_][
2300: 41 2d 5a 61 2d 7a 30 2d 39 2e 5f 2d 5d 7b 30 2c  A-Za-z0-9._-]{0,
2310: 39 39 7d 5c 7a 2f 29 20 26 26 20 46 69 6c 65 2e  99}\z/) && File.
2320: 66 69 6c 65 3f 28 70 61 74 68 29 0a 20 20 20 20  file?(path).    
2330: 72 61 69 73 65 20 52 65 66 75 73 65 64 2e 6e 65  raise Refused.ne
2340: 77 28 22 69 6e 76 61 6c 69 64 20 6f 63 78 5f 72  w("invalid ocx_r
2350: 65 63 65 69 70 74 22 29 20 75 6e 6c 65 73 73 20  eceipt") unless 
2360: 72 65 71 75 65 73 74 2e 6f 63 78 5f 72 65 63 65  request.ocx_rece
2370: 69 70 74 20 3d 3d 20 31 0a 20 20 20 20 72 61 69  ipt == 1.    rai
2380: 73 65 20 52 65 66 75 73 65 64 2e 6e 65 77 28 22  se Refused.new("
2390: 69 6e 76 61 6c 69 64 20 61 72 74 69 66 61 63 74  invalid artifact
23a0: 73 22 29 20 75 6e 6c 65 73 73 20 72 65 71 75 65  s") unless reque
23b0: 73 74 2e 61 72 74 69 66 61 63 74 73 2e 73 69 7a  st.artifacts.siz
23c0: 65 20 3c 3d 20 36 34 20 26 26 20 72 65 71 75 65  e <= 64 && reque
23d0: 73 74 2e 61 72 74 69 66 61 63 74 73 2e 61 6c 6c  st.artifacts.all
23e0: 3f 28 26 2e 6d 61 74 63 68 65 73 3f 28 2f 5c 41  ?(&.matches?(/\A
23f0: 28 5b 30 2d 39 61 2d 66 5d 7b 34 30 7d 7c 5b 30  ([0-9a-f]{40}|[0
2400: 2d 39 61 2d 66 5d 7b 36 34 7d 29 5c 7a 2f 29 29  -9a-f]{64})\z/))
2410: 0a 20 20 20 20 72 61 69 73 65 20 52 65 66 75 73  .    raise Refus
2420: 65 64 2e 6e 65 77 28 22 69 6e 76 61 6c 69 64 20  ed.new("invalid 
2430: 63 61 70 61 62 69 6c 69 74 69 65 73 5f 6f 66 22  capabilities_of"
2440: 29 20 69 66 20 72 65 71 75 65 73 74 2e 63 61 70  ) if request.cap
2450: 61 62 69 6c 69 74 69 65 73 5f 6f 66 2e 74 72 79  abilities_of.try
2460: 20 7b 20 7c 75 7c 20 75 2e 73 69 7a 65 20 3e 20   { |u| u.size > 
2470: 31 30 30 20 7c 7c 20 75 2e 65 61 63 68 5f 63 68  100 || u.each_ch
2480: 61 72 2e 61 6e 79 3f 28 26 2e 63 6f 6e 74 72 6f  ar.any?(&.contro
2490: 6c 3f 29 20 7d 0a 20 20 20 20 69 66 20 21 69 64  l?) }.    if !id
24a0: 65 6e 74 69 74 79 2e 63 61 70 61 62 69 6c 69 74  entity.capabilit
24b0: 69 65 73 2e 69 6e 63 6c 75 64 65 73 3f 28 27 6f  ies.includes?('o
24c0: 27 29 20 23 20 6e 6f 20 52 65 61 64 3a 20 6e 6f  ') # no Read: no
24d0: 74 68 69 6e 67 20 63 6f 75 6e 74 73 20 61 73 20  thing counts as 
24e0: 72 65 63 65 69 76 65 64 2c 20 61 6e 64 20 6e 6f  received, and no
24f0: 74 68 69 6e 67 20 69 73 20 72 65 61 64 0a 20 20  thing is read.  
2500: 20 20 20 20 41 6e 73 77 65 72 2e 6e 65 77 28 72      Answer.new(r
2510: 65 71 75 65 73 74 2e 61 72 74 69 66 61 63 74 73  equest.artifacts
2520: 2e 6d 61 70 20 7b 20 7c 68 7c 20 41 72 74 69 66  .map { |h| Artif
2530: 61 63 74 2e 6e 65 77 28 68 29 20 7d 2c 20 72 65  act.new(h) }, re
2540: 71 75 65 73 74 2e 63 61 70 61 62 69 6c 69 74 69  quest.capabiliti
2550: 65 73 5f 6f 66 2e 74 72 79 20 7b 20 7c 75 7c 20  es_of.try { |u| 
2560: 55 73 65 72 2e 6e 65 77 28 75 29 20 7d 29 2e 74  User.new(u) }).t
2570: 6f 5f 6a 73 6f 6e 0a 20 20 20 20 65 6c 73 65 0a  o_json.    else.
2580: 20 20 20 20 20 20 67 69 76 65 5f 75 70 20 3d 20        give_up = 
2590: 54 69 6d 65 2e 75 74 63 20 2b 20 4c 4f 43 4b 5f  Time.utc + LOCK_
25a0: 57 41 49 54 0a 20 20 20 20 20 20 6c 6f 6f 70 20  WAIT.      loop 
25b0: 64 6f 0a 20 20 20 20 20 20 20 20 62 72 65 61 6b  do.        break
25c0: 20 72 65 61 64 28 70 61 74 68 2c 20 69 64 65 6e   read(path, iden
25d0: 74 69 74 79 2e 63 61 70 61 62 69 6c 69 74 69 65  tity.capabilitie
25e0: 73 2c 20 72 65 71 75 65 73 74 29 2e 74 6f 5f 6a  s, request).to_j
25f0: 73 6f 6e 0a 20 20 20 20 20 20 72 65 73 63 75 65  son.      rescue
2600: 20 4c 6f 63 6b 65 64 0a 20 20 20 20 20 20 20 20   Locked.        
2610: 72 61 69 73 65 20 49 4f 3a 3a 45 72 72 6f 72 2e  raise IO::Error.
2620: 6e 65 77 28 22 73 71 6c 69 74 65 20 65 72 72 6f  new("sqlite erro
2630: 72 20 35 3a 20 64 61 74 61 62 61 73 65 20 69 73  r 5: database is
2640: 20 6c 6f 63 6b 65 64 22 29 20 69 66 20 54 69 6d   locked") if Tim
2650: 65 2e 75 74 63 20 3e 3d 20 67 69 76 65 5f 75 70  e.utc >= give_up
2660: 0a 20 20 20 20 20 20 20 20 73 6c 65 65 70 20 35  .        sleep 5
2670: 30 2e 6d 69 6c 6c 69 73 65 63 6f 6e 64 73 20 23  0.milliseconds #
2680: 20 77 61 69 74 69 6e 67 20 68 65 72 65 20 6c 65   waiting here le
2690: 74 73 20 6f 74 68 65 72 20 63 6f 6e 6e 65 63 74  ts other connect
26a0: 69 6f 6e 73 20 67 6f 20 6f 6e 0a 20 20 20 20 20  ions go on.     
26b0: 20 65 6e 64 0a 20 20 20 20 65 6e 64 0a 20 20 72   end.    end.  r
26c0: 65 73 63 75 65 20 65 78 20 3a 20 52 65 66 75 73  escue ex : Refus
26d0: 65 64 20 7c 20 4a 53 4f 4e 3a 3a 53 65 72 69 61  ed | JSON::Seria
26e0: 6c 69 7a 61 62 6c 65 45 72 72 6f 72 20 7c 20 4a  lizableError | J
26f0: 53 4f 4e 3a 3a 50 61 72 73 65 45 78 63 65 70 74  SON::ParseExcept
2700: 69 6f 6e 0a 20 20 20 20 6f 75 74 63 6f 6d 65 20  ion.    outcome 
2710: 3d 20 22 72 65 66 75 73 65 64 22 0a 20 20 20 20  = "refused".    
2720: 7b 72 65 66 75 73 65 64 3a 20 65 78 2e 69 73 5f  {refused: ex.is_
2730: 61 3f 28 52 65 66 75 73 65 64 29 20 3f 20 65 78  a?(Refused) ? ex
2740: 2e 6d 65 73 73 61 67 65 20 3a 20 22 69 6e 76 61  .message : "inva
2750: 6c 69 64 20 4a 53 4f 4e 3a 20 23 7b 65 78 2e 6d  lid JSON: #{ex.m
2760: 65 73 73 61 67 65 2e 74 6f 5f 73 2e 6c 69 6e 65  essage.to_s.line
2770: 73 2e 66 69 72 73 74 3f 7d 22 7d 2e 74 6f 5f 6a  s.first?}"}.to_j
2780: 73 6f 6e 0a 20 20 72 65 73 63 75 65 20 65 78 0a  son.  rescue ex.
2790: 20 20 20 20 6f 75 74 63 6f 6d 65 20 3d 20 22 75      outcome = "u
27a0: 6e 61 76 61 69 6c 61 62 6c 65 22 0a 20 20 20 20  navailable".    
27b0: 7b 75 6e 61 76 61 69 6c 61 62 6c 65 3a 20 65 78  {unavailable: ex
27c0: 2e 69 73 5f 61 3f 28 49 4f 3a 3a 45 72 72 6f 72  .is_a?(IO::Error
27d0: 29 20 3f 20 65 78 2e 6d 65 73 73 61 67 65 20 3a  ) ? ex.message :
27e0: 20 22 69 6e 74 65 72 6e 61 6c 20 65 72 72 6f 72   "internal error
27f0: 22 2c 20 72 65 74 72 79 5f 61 66 74 65 72 3a 20  ", retry_after: 
2800: 31 7d 2e 74 6f 5f 6a 73 6f 6e 0a 20 20 65 6e 64  1}.to_json.  end
2810: 0a 20 20 73 6f 63 6b 65 74 2e 77 72 69 74 65 5f  .  socket.write_
2820: 74 69 6d 65 6f 75 74 20 3d 20 32 2e 73 65 63 6f  timeout = 2.seco
2830: 6e 64 73 0a 20 20 73 6f 63 6b 65 74 2e 70 75 74  nds.  socket.put
2840: 73 28 61 6e 73 77 65 72 29 0a 72 65 73 63 75 65  s(answer).rescue
2850: 20 49 4f 3a 3a 45 72 72 6f 72 20 23 20 74 68 65   IO::Error # the
2860: 20 63 61 6c 6c 65 72 20 6c 65 66 74 0a 65 6e 73   caller left.ens
2870: 75 72 65 0a 20 20 73 6f 63 6b 65 74 2e 63 6c 6f  ure.  socket.clo
2880: 73 65 0a 20 20 53 54 44 45 52 52 2e 70 75 74 73  se.  STDERR.puts
2890: 20 22 23 7b 54 69 6d 65 2e 75 74 63 2e 74 6f 5f   "#{Time.utc.to_
28a0: 72 66 63 33 33 33 39 7d 20 23 7b 6e 61 6d 65 7d  rfc3339} #{name}
28b0: 20 23 7b 6f 75 74 63 6f 6d 65 7d 20 23 7b 28 54   #{outcome} #{(T
28c0: 69 6d 65 2e 75 74 63 20 2d 20 73 74 61 72 74 65  ime.utc - starte
28d0: 64 29 2e 74 6f 74 61 6c 5f 6d 69 6c 6c 69 73 65  d).total_millise
28e0: 63 6f 6e 64 73 2e 74 6f 5f 69 7d 6d 73 22 0a 65  conds.to_i}ms".e
28f0: 6e 64 0a 0a 23 20 4c 69 73 74 65 6e 20 61 74 20  nd..# Listen at 
2900: 74 68 65 20 66 69 78 65 64 20 73 6f 63 6b 65 74  the fixed socket
2910: 3b 20 65 61 63 68 20 63 6f 6e 6e 65 63 74 69 6f  ; each connectio
2920: 6e 20 69 73 20 73 65 72 76 65 64 20 6f 6e 20 69  n is served on i
2930: 74 73 20 6f 77 6e 2c 20 73 6f 20 6f 6e 65 20 6c  ts own, so one l
2940: 6f 63 6b 20 6e 65 76 65 72 20 68 6f 6c 64 73 20  ock never holds 
2950: 75 70 20 61 6e 6f 74 68 65 72 2e 0a 46 69 6c 65  up another..File
2960: 2e 64 65 6c 65 74 65 28 53 4f 43 4b 45 54 29 20  .delete(SOCKET) 
2970: 69 66 20 46 69 6c 65 2e 65 78 69 73 74 73 3f 28  if File.exists?(
2980: 53 4f 43 4b 45 54 29 0a 73 65 72 76 65 72 20 3d  SOCKET).server =
2990: 20 55 4e 49 58 53 65 72 76 65 72 2e 6e 65 77 28   UNIXServer.new(
29a0: 53 4f 43 4b 45 54 29 0a 46 69 6c 65 2e 63 68 6d  SOCKET).File.chm
29b0: 6f 64 28 53 4f 43 4b 45 54 2c 20 30 6f 36 30 30  od(SOCKET, 0o600
29c0: 29 0a 77 68 69 6c 65 20 73 6f 63 6b 65 74 20 3d  ).while socket =
29d0: 20 73 65 72 76 65 72 2e 61 63 63 65 70 74 3f 0a   server.accept?.
29e0: 20 20 73 70 61 77 6e 20 73 65 72 76 65 28 73 6f    spawn serve(so
29f0: 63 6b 65 74 2c 20 54 69 6d 65 2e 75 74 63 29 0a  cket, Time.utc).
2a00: 65 6e 64 0a 00 00 00 00 00 00 00 00 00 00 00 00  end.............
2a10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2a20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2a30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2a40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2a50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2a60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2a70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2a80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2a90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2aa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2ab0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2ac0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2ad0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2ae0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2af0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2b10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2b20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2b30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2b40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2b50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2b60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2b70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2b80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2b90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2ba0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2bb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2bc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2bd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2be0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2bf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2c00: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69  scripts-for-revi
2c10: 65 77 2d 33 2f 73 74 61 72 74 2d 66 6f 73 73 69  ew-3/start-fossi
2c20: 6c 2e 63 72 00 00 00 00 00 00 00 00 00 00 00 00  l.cr............
2c30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2c40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2c50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2c60: 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30  ....0000644.0000
2c70: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30  000.0000000.0000
2c80: 30 30 30 34 37 36 33 00 31 35 32 37 30 36 36 34  0004763.15270664
2c90: 36 31 33 00 30 31 36 34 37 32 00 20 30 00 00 00  613.016472. 0...
2ca0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2cb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2cc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2cd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2ce0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2cf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2d00: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00  .ustar  .root...
2d10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2d20: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00  .........root...
2d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2d50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2d70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2d90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2dc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2dd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2de0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2df0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
2e00: 23 20 41 67 65 6e 74 73 3a 20 64 6f 20 6e 6f 74  # Agents: do not
2e10: 20 63 68 61 6e 67 65 20 74 68 69 73 20 66 69 6c   change this fil
2e20: 65 20 6f 6e 20 61 6e 79 20 62 72 61 6e 63 68 20  e on any branch 
2e30: 77 69 74 68 6f 75 74 20 74 68 65 20 6d 61 69 6e  without the main
2e40: 74 61 69 6e 65 72 27 73 20 65 78 70 6c 69 63 69  tainer's explici
2e50: 74 20 61 70 70 72 6f 76 61 6c 20 6f 6e 20 74 68  t approval on th
2e60: 65 20 72 65 63 6f 72 64 2e 0a 23 20 53 74 61 72  e record..# Star
2e70: 74 20 46 6f 73 73 69 6c 3b 20 73 65 74 74 69 6e  t Fossil; settin
2e80: 67 73 20 61 72 65 20 69 6e 20 74 68 65 20 73 65  gs are in the se
2e90: 72 76 65 72 73 2e 6a 73 6f 6e 20 62 65 73 69 64  rvers.json besid
2ea0: 65 20 74 68 69 73 20 70 72 6f 67 72 61 6d 2e 0a  e this program..
2eb0: 23 20 54 68 65 20 6d 61 69 6e 74 61 69 6e 65 72  # The maintainer
2ec0: 20 72 75 6e 73 20 62 69 6e 2f 73 74 61 72 74 2d   runs bin/start-
2ed0: 66 6f 73 73 69 6c 20 61 66 74 65 72 20 73 68 61  fossil after sha
2ee0: 72 64 73 20 62 75 69 6c 64 20 73 74 61 72 74 2d  rds build start-
2ef0: 66 6f 73 73 69 6c 2e 0a 72 65 71 75 69 72 65 20  fossil..require 
2f00: 22 6a 73 6f 6e 22 0a 0a 73 74 72 75 63 74 20 46  "json"..struct F
2f10: 6f 73 73 69 6c 53 65 72 76 65 72 0a 20 20 69 6e  ossilServer.  in
2f20: 63 6c 75 64 65 20 4a 53 4f 4e 3a 3a 53 65 72 69  clude JSON::Seri
2f30: 61 6c 69 7a 61 62 6c 65 0a 20 20 67 65 74 74 65  alizable.  gette
2f40: 72 20 66 6f 73 73 69 6c 20 3a 20 53 74 72 69 6e  r fossil : Strin
2f50: 67 20 3d 20 22 66 6f 73 73 69 6c 22 0a 20 20 67  g = "fossil".  g
2f60: 65 74 74 65 72 20 61 72 67 73 20 3a 20 41 72 72  etter args : Arr
2f70: 61 79 28 53 74 72 69 6e 67 29 0a 20 20 67 65 74  ay(String).  get
2f80: 74 65 72 20 73 68 6f 77 5f 63 61 70 61 62 69 6c  ter show_capabil
2f90: 69 74 69 65 73 20 3a 20 42 6f 6f 6c 20 3d 20 66  ities : Bool = f
2fa0: 61 6c 73 65 0a 65 6e 64 0a 0a 73 74 72 75 63 74  alse.end..struct
2fb0: 20 4f 43 58 0a 20 20 69 6e 63 6c 75 64 65 20 4a   OCX.  include J
2fc0: 53 4f 4e 3a 3a 53 65 72 69 61 6c 69 7a 61 62 6c  SON::Serializabl
2fd0: 65 0a 20 20 67 65 74 74 65 72 20 6c 61 75 6e 63  e.  getter launc
2fe0: 68 65 72 20 3a 20 53 74 72 69 6e 67 3f 0a 65 6e  her : String?.en
2ff0: 64 0a 0a 73 74 72 75 63 74 20 53 65 72 76 65 72  d..struct Server
3000: 0a 20 20 69 6e 63 6c 75 64 65 20 4a 53 4f 4e 3a  .  include JSON:
3010: 3a 53 65 72 69 61 6c 69 7a 61 62 6c 65 0a 20 20  :Serializable.  
3020: 67 65 74 74 65 72 20 66 6f 73 73 69 6c 5f 73 65  getter fossil_se
3030: 72 76 65 72 20 3a 20 46 6f 73 73 69 6c 53 65 72  rver : FossilSer
3040: 76 65 72 3f 0a 20 20 67 65 74 74 65 72 20 6f 63  ver?.  getter oc
3050: 78 20 3a 20 4f 43 58 3f 0a 65 6e 64 0a 0a 23 20  x : OCX?.end..# 
3060: 53 65 6c 65 63 74 20 65 61 63 68 20 70 61 72 74  Select each part
3070: 20 66 72 6f 6d 20 74 68 69 73 20 68 6f 73 74 2c   from this host,
3080: 20 74 68 65 6e 20 74 68 65 20 64 65 66 61 75 6c   then the defaul
3090: 74 20 65 6e 74 72 79 2e 0a 66 69 6c 65 20 3d 20  t entry..file = 
30a0: 46 69 6c 65 2e 6a 6f 69 6e 28 46 69 6c 65 2e 64  File.join(File.d
30b0: 69 72 6e 61 6d 65 28 50 72 6f 63 65 73 73 2e 65  irname(Process.e
30c0: 78 65 63 75 74 61 62 6c 65 5f 70 61 74 68 2e 6e  xecutable_path.n
30d0: 6f 74 5f 6e 69 6c 21 29 2c 20 22 73 65 72 76 65  ot_nil!), "serve
30e0: 72 73 2e 6a 73 6f 6e 22 29 0a 62 65 67 69 6e 0a  rs.json").begin.
30f0: 20 20 73 65 72 76 65 72 73 20 3d 20 48 61 73 68    servers = Hash
3100: 28 53 74 72 69 6e 67 2c 20 4a 53 4f 4e 3a 3a 41  (String, JSON::A
3110: 6e 79 29 2e 66 72 6f 6d 5f 6a 73 6f 6e 28 46 69  ny).from_json(Fi
3120: 6c 65 2e 72 65 61 64 28 66 69 6c 65 29 29 0a 20  le.read(file)). 
3130: 20 6c 6f 63 61 6c 20 3d 20 73 65 72 76 65 72 73   local = servers
3140: 5b 53 79 73 74 65 6d 2e 68 6f 73 74 6e 61 6d 65  [System.hostname
3150: 5d 3f 2e 74 72 79 20 7b 20 7c 65 6e 74 72 79 7c  ]?.try { |entry|
3160: 20 53 65 72 76 65 72 2e 66 72 6f 6d 5f 6a 73 6f   Server.from_jso
3170: 6e 28 65 6e 74 72 79 2e 74 6f 5f 6a 73 6f 6e 29  n(entry.to_json)
3180: 20 7d 0a 20 20 64 65 66 61 75 6c 74 20 3d 20 73   }.  default = s
3190: 65 72 76 65 72 73 5b 22 2a 22 5d 3f 2e 74 72 79  ervers["*"]?.try
31a0: 20 7b 20 7c 65 6e 74 72 79 7c 20 53 65 72 76 65   { |entry| Serve
31b0: 72 2e 66 72 6f 6d 5f 6a 73 6f 6e 28 65 6e 74 72  r.from_json(entr
31c0: 79 2e 74 6f 5f 6a 73 6f 6e 29 20 7d 0a 20 20 73  y.to_json) }.  s
31d0: 65 74 74 69 6e 67 73 20 3d 20 6c 6f 63 61 6c 2e  ettings = local.
31e0: 74 72 79 28 26 2e 66 6f 73 73 69 6c 5f 73 65 72  try(&.fossil_ser
31f0: 76 65 72 29 20 7c 7c 20 64 65 66 61 75 6c 74 2e  ver) || default.
3200: 74 72 79 28 26 2e 66 6f 73 73 69 6c 5f 73 65 72  try(&.fossil_ser
3210: 76 65 72 29 20 7c 7c 20 61 62 6f 72 74 20 22 23  ver) || abort "#
3220: 7b 66 69 6c 65 7d 3a 20 6d 69 73 73 69 6e 67 20  {file}: missing 
3230: 66 6f 73 73 69 6c 5f 73 65 72 76 65 72 22 2c 20  fossil_server", 
3240: 32 0a 72 65 73 63 75 65 20 65 78 20 3a 20 49 4f  2.rescue ex : IO
3250: 3a 3a 45 72 72 6f 72 20 7c 20 4a 53 4f 4e 3a 3a  ::Error | JSON::
3260: 50 61 72 73 65 45 78 63 65 70 74 69 6f 6e 0a 20  ParseException. 
3270: 20 61 62 6f 72 74 20 22 23 7b 66 69 6c 65 7d 3a   abort "#{file}:
3280: 20 63 61 6e 6e 6f 74 20 72 65 61 64 20 66 6f 73   cannot read fos
3290: 73 69 6c 5f 73 65 72 76 65 72 3a 20 23 7b 65 78  sil_server: #{ex
32a0: 2e 6d 65 73 73 61 67 65 2e 74 6f 5f 73 2e 6c 69  .message.to_s.li
32b0: 6e 65 73 2e 66 69 72 73 74 7d 22 2c 20 32 0a 65  nes.first}", 2.e
32c0: 6e 64 0a 62 61 73 65 64 69 72 20 3d 20 46 69 6c  nd.basedir = Fil
32d0: 65 2e 72 65 61 6c 70 61 74 68 28 46 69 6c 65 2e  e.realpath(File.
32e0: 6a 6f 69 6e 28 46 69 6c 65 2e 64 69 72 6e 61 6d  join(File.dirnam
32f0: 65 28 50 72 6f 63 65 73 73 2e 65 78 65 63 75 74  e(Process.execut
3300: 61 62 6c 65 5f 70 61 74 68 2e 6e 6f 74 5f 6e 69  able_path.not_ni
3310: 6c 21 29 2c 20 22 2e 2e 2f 2e 2e 22 29 29 0a 0a  l!), "../.."))..
3320: 64 65 66 20 65 78 70 61 6e 64 28 74 65 78 74 20  def expand(text 
3330: 3a 20 53 74 72 69 6e 67 2c 20 62 61 73 65 64 69  : String, basedi
3340: 72 20 3a 20 53 74 72 69 6e 67 29 20 3a 20 53 74  r : String) : St
3350: 72 69 6e 67 0a 20 20 74 65 78 74 2e 67 73 75 62  ring.  text.gsub
3360: 28 2f 5c 24 48 4f 4d 45 7c 5c 24 42 41 53 45 44  (/\$HOME|\$BASED
3370: 49 52 2f 29 20 7b 20 7c 6e 61 6d 65 7c 20 6e 61  IR/) { |name| na
3380: 6d 65 20 3d 3d 20 22 24 48 4f 4d 45 22 20 3f 20  me == "$HOME" ? 
3390: 45 4e 56 5b 22 48 4f 4d 45 22 5d 20 3a 20 62 61  ENV["HOME"] : ba
33a0: 73 65 64 69 72 20 7d 0a 65 6e 64 0a 0a 61 72 67  sedir }.end..arg
33b0: 73 20 3d 20 73 65 74 74 69 6e 67 73 2e 61 72 67  s = settings.arg
33c0: 73 2e 6d 61 70 20 7b 20 7c 61 72 67 7c 20 65 78  s.map { |arg| ex
33d0: 70 61 6e 64 28 61 72 67 2c 20 62 61 73 65 64 69  pand(arg, basedi
33e0: 72 29 20 7d 0a 0a 23 20 53 68 6f 77 20 74 68 65  r) }..# Show the
33f0: 20 68 6f 73 74 20 61 6e 64 20 62 61 73 65 20 64   host and base d
3400: 69 72 65 63 74 6f 72 79 3b 20 73 68 6f 77 20 74  irectory; show t
3410: 68 65 20 6c 6f 63 61 6c 20 46 6f 73 73 69 6c 27  he local Fossil'
3420: 73 20 66 69 6c 65 20 63 61 70 61 62 69 6c 69 74  s file capabilit
3430: 69 65 73 2e 0a 68 6f 73 74 20 3d 20 53 79 73 74  ies..host = Syst
3440: 65 6d 2e 68 6f 73 74 6e 61 6d 65 0a 70 75 74 73  em.hostname.puts
3450: 20 22 52 75 6e 6e 69 6e 67 20 6f 6e 20 23 7b 68   "Running on #{h
3460: 6f 73 74 7d 22 0a 70 75 74 73 20 22 52 75 6e 6e  ost}".puts "Runn
3470: 69 6e 67 20 66 72 6f 6d 20 23 7b 62 61 73 65 64  ing from #{based
3480: 69 72 7d 22 0a 69 66 20 73 65 74 74 69 6e 67 73  ir}".if settings
3490: 2e 73 68 6f 77 5f 63 61 70 61 62 69 6c 69 74 69  .show_capabiliti
34a0: 65 73 0a 20 20 66 6f 75 6e 64 20 3d 20 50 72 6f  es.  found = Pro
34b0: 63 65 73 73 2e 66 69 6e 64 5f 65 78 65 63 75 74  cess.find_execut
34c0: 61 62 6c 65 28 22 66 6f 73 73 69 6c 22 29 0a 20  able("fossil"). 
34d0: 20 50 72 6f 63 65 73 73 2e 72 75 6e 28 22 67 65   Process.run("ge
34e0: 74 63 61 70 22 2c 20 5b 66 6f 75 6e 64 20 3f 20  tcap", [found ? 
34f0: 46 69 6c 65 2e 72 65 61 6c 70 61 74 68 28 66 6f  File.realpath(fo
3500: 75 6e 64 29 20 3a 20 22 22 5d 2c 20 6f 75 74 70  und) : ""], outp
3510: 75 74 3a 20 3a 69 6e 68 65 72 69 74 2c 20 65 72  ut: :inherit, er
3520: 72 6f 72 3a 20 3a 69 6e 68 65 72 69 74 29 0a 65  ror: :inherit).e
3530: 6e 64 0a 0a 23 20 57 68 65 72 65 20 74 68 69 73  nd..# Where this
3540: 20 68 6f 73 74 20 73 65 72 76 65 73 20 4f 43 58   host serves OCX
3550: 27 73 20 70 61 67 65 3a 20 6c 69 6e 6b 20 69 74  's page: link it
3560: 20 74 6f 20 74 68 65 20 6c 61 75 6e 63 68 65 72   to the launcher
3570: 20 6f 6e 6c 79 20 77 68 65 72 65 20 6e 6f 74 68   only where noth
3580: 69 6e 67 20 69 73 20 74 68 65 72 65 2c 0a 23 20  ing is there,.# 
3590: 6c 65 61 76 69 6e 67 20 61 6e 20 65 78 69 73 74  leaving an exist
35a0: 69 6e 67 20 6c 69 6e 6b 20 61 6c 6f 6e 65 2c 20  ing link alone, 
35b0: 73 6f 20 61 20 72 6f 6c 6c 62 61 63 6b 20 68 6f  so a rollback ho
35c0: 6c 64 73 2e 0a 6c 61 75 6e 63 68 65 72 20 3d 20  lds..launcher = 
35d0: 28 6c 6f 63 61 6c 2e 74 72 79 28 26 2e 6f 63 78  (local.try(&.ocx
35e0: 29 20 7c 7c 20 64 65 66 61 75 6c 74 2e 74 72 79  ) || default.try
35f0: 28 26 2e 6f 63 78 29 29 2e 74 72 79 28 26 2e 6c  (&.ocx)).try(&.l
3600: 61 75 6e 63 68 65 72 29 0a 69 66 20 28 69 6e 64  auncher).if (ind
3610: 65 78 20 3d 20 61 72 67 73 2e 69 6e 64 65 78 28  ex = args.index(
3620: 22 2d 2d 65 78 74 72 6f 6f 74 22 29 29 20 26 26  "--extroot")) &&
3630: 20 6c 61 75 6e 63 68 65 72 20 26 26 20 46 69 6c   launcher && Fil
3640: 65 2e 65 78 69 73 74 73 3f 28 6c 61 75 6e 63 68  e.exists?(launch
3650: 65 72 29 0a 20 20 6c 69 6e 6b 20 3d 20 46 69 6c  er).  link = Fil
3660: 65 2e 6a 6f 69 6e 28 61 72 67 73 5b 69 6e 64 65  e.join(args[inde
3670: 78 20 2b 20 31 5d 2c 20 22 6f 63 78 32 2d 63 67  x + 1], "ocx2-cg
3680: 69 22 29 0a 20 20 69 66 20 46 69 6c 65 2e 73 79  i").  if File.sy
3690: 6d 6c 69 6e 6b 3f 28 6c 69 6e 6b 29 20 7c 7c 20  mlink?(link) || 
36a0: 46 69 6c 65 2e 65 78 69 73 74 73 3f 28 6c 69 6e  File.exists?(lin
36b0: 6b 29 0a 20 20 20 20 70 75 74 73 20 22 23 7b 6c  k).    puts "#{l
36c0: 69 6e 6b 7d 20 69 73 20 74 68 65 72 65 20 61 6c  ink} is there al
36d0: 72 65 61 64 79 3b 20 6c 65 66 74 20 61 73 20 69  ready; left as i
36e0: 74 20 69 73 22 0a 20 20 65 6c 73 65 0a 20 20 20  t is".  else.   
36f0: 20 46 69 6c 65 2e 73 79 6d 6c 69 6e 6b 28 6c 61   File.symlink(la
3700: 75 6e 63 68 65 72 2c 20 6c 69 6e 6b 29 0a 20 20  uncher, link).  
3710: 20 20 70 75 74 73 20 22 6c 69 6e 6b 65 64 20 23    puts "linked #
3720: 7b 6c 69 6e 6b 7d 20 74 6f 20 23 7b 6c 61 75 6e  {link} to #{laun
3730: 63 68 65 72 7d 22 0a 20 20 65 6e 64 0a 65 6e 64  cher}".  end.end
3740: 0a 0a 23 20 52 75 6e 20 46 6f 73 73 69 6c 20 61  ..# Run Fossil a
3750: 6e 64 20 72 65 74 75 72 6e 20 69 74 73 20 73 74  nd return its st
3760: 61 74 75 73 2e 0a 73 74 61 74 75 73 20 3d 20 50  atus..status = P
3770: 72 6f 63 65 73 73 2e 72 75 6e 28 65 78 70 61 6e  rocess.run(expan
3780: 64 28 73 65 74 74 69 6e 67 73 2e 66 6f 73 73 69  d(settings.fossi
3790: 6c 2c 20 62 61 73 65 64 69 72 29 2c 20 61 72 67  l, basedir), arg
37a0: 73 2c 20 69 6e 70 75 74 3a 20 3a 69 6e 68 65 72  s, input: :inher
37b0: 69 74 2c 20 6f 75 74 70 75 74 3a 20 3a 69 6e 68  it, output: :inh
37c0: 65 72 69 74 2c 20 65 72 72 6f 72 3a 20 3a 69 6e  erit, error: :in
37d0: 68 65 72 69 74 29 0a 65 78 69 74 20 73 74 61 74  herit).exit stat
37e0: 75 73 2e 65 78 69 74 5f 63 6f 64 65 3f 20 7c 7c  us.exit_code? ||
37f0: 20 31 0a 00 00 00 00 00 00 00 00 00 00 00 00 00   1..............
3800: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69  scripts-for-revi
3810: 65 77 2d 33 2f 73 74 61 72 74 2d 6f 63 78 2e 63  ew-3/start-ocx.c
3820: 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  r...............
3830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
3840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
3850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
3860: 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30  ....0000644.0000
3870: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30  000.0000000.0000
3880: 30 30 31 36 35 32 36 00 31 35 32 37 30 36 36 34  0016526.15270664
3890: 36 31 33 00 30 31 35 37 36 34 00 20 30 00 00 00  613.015764. 0...
38a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
38b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
38c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
38d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
38e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
38f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
3900: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00  .ustar  .root...
3910: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
3920: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00  .........root...
3930: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
3940: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
3950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
3960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
3970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
3980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
3990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
39a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
39b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
39c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
39d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
39e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
39f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
3a00: 23 20 41 67 65 6e 74 73 3a 20 64 6f 20 6e 6f 74  # Agents: do not
3a10: 20 63 68 61 6e 67 65 20 74 68 69 73 20 66 69 6c   change this fil
3a20: 65 20 6f 6e 20 61 6e 79 20 62 72 61 6e 63 68 20  e on any branch 
3a30: 77 69 74 68 6f 75 74 20 74 68 65 20 6d 61 69 6e  without the main
3a40: 74 61 69 6e 65 72 27 73 20 65 78 70 6c 69 63 69  tainer's explici
3a50: 74 20 61 70 70 72 6f 76 61 6c 20 6f 6e 20 74 68  t approval on th
3a60: 65 20 72 65 63 6f 72 64 2e 0a 23 20 46 6f 73 73  e record..# Foss
3a70: 69 6c 20 61 6e 64 20 73 79 73 74 65 6d 64 20 73  il and systemd s
3a80: 74 61 72 74 20 74 68 69 73 3b 20 73 65 74 74 69  tart this; setti
3a90: 6e 67 73 20 61 72 65 20 69 6e 20 74 68 65 20 73  ngs are in the s
3aa0: 65 72 76 65 72 73 2e 6a 73 6f 6e 20 62 65 73 69  ervers.json besi
3ab0: 64 65 20 74 68 69 73 20 70 72 6f 67 72 61 6d 2e  de this program.
3ac0: 0a 23 20 42 75 69 6c 64 20 62 65 73 69 64 65 20  .# Build beside 
3ad0: 72 65 63 65 69 70 74 73 20 77 69 74 68 20 73 68  receipts with sh
3ae0: 61 72 64 73 20 62 75 69 6c 64 20 73 74 61 72 74  ards build start
3af0: 2d 6f 63 78 20 72 65 63 65 69 70 74 73 2e 0a 72  -ocx receipts..r
3b00: 65 71 75 69 72 65 20 22 6a 73 6f 6e 22 0a 72 65  equire "json".re
3b10: 71 75 69 72 65 20 22 73 6f 63 6b 65 74 22 0a 0a  quire "socket"..
3b20: 23 20 41 20 68 6f 73 74 20 74 68 61 74 20 6f 6e  # A host that on
3b30: 6c 79 20 72 75 6e 73 20 74 68 65 20 64 61 65 6d  ly runs the daem
3b40: 6f 6e 2c 20 73 75 63 68 20 61 73 20 61 20 62 75  on, such as a bu
3b50: 69 6c 64 20 6d 61 63 68 69 6e 65 2c 20 6c 65 61  ild machine, lea
3b60: 76 65 73 20 6f 75 74 20 74 68 65 0a 23 20 77 65  ves out the.# we
3b70: 62 20 70 61 67 65 27 73 20 61 6e 64 20 74 68 65  b page's and the
3b80: 20 72 65 63 65 69 70 74 20 73 65 72 76 69 63 65   receipt service
3b90: 27 73 20 73 65 74 74 69 6e 67 73 2e 0a 73 74 72  's settings..str
3ba0: 75 63 74 20 4f 43 58 0a 20 20 69 6e 63 6c 75 64  uct OCX.  includ
3bb0: 65 20 4a 53 4f 4e 3a 3a 53 65 72 69 61 6c 69 7a  e JSON::Serializ
3bc0: 61 62 6c 65 0a 20 20 67 65 74 74 65 72 20 72 65  able.  getter re
3bd0: 70 6f 73 69 74 6f 72 69 65 73 20 3a 20 53 74 72  positories : Str
3be0: 69 6e 67 3f 0a 20 20 67 65 74 74 65 72 20 73 6f  ing?.  getter so
3bf0: 63 6b 65 74 5f 64 69 72 20 3a 20 53 74 72 69 6e  cket_dir : Strin
3c00: 67 3f 0a 20 20 67 65 74 74 65 72 20 72 65 6c 65  g?.  getter rele
3c10: 61 73 65 73 20 3a 20 53 74 72 69 6e 67 3f 0a 20  ases : String?. 
3c20: 20 67 65 74 74 65 72 20 64 61 65 6d 6f 6e 5f 72   getter daemon_r
3c30: 6f 6f 74 20 3a 20 53 74 72 69 6e 67 3f 0a 20 20  oot : String?.  
3c40: 67 65 74 74 65 72 20 68 6f 6d 65 73 20 3d 20 7b  getter homes = {
3c50: 7d 20 6f 66 20 53 74 72 69 6e 67 20 3d 3e 20 53  } of String => S
3c60: 74 72 69 6e 67 20 23 20 72 65 61 64 2d 77 72 69  tring # read-wri
3c70: 74 65 2c 20 68 6f 73 74 20 70 61 74 68 20 3d 3e  te, host path =>
3c80: 20 69 6e 73 69 64 65 20 74 68 65 20 64 61 65 6d   inside the daem
3c90: 6f 6e 0a 20 20 67 65 74 74 65 72 20 74 6f 6f 6c  on.  getter tool
3ca0: 73 20 3d 20 7b 7d 20 6f 66 20 53 74 72 69 6e 67  s = {} of String
3cb0: 20 3d 3e 20 53 74 72 69 6e 67 20 23 20 72 65 61   => String # rea
3cc0: 64 2d 6f 6e 6c 79 2c 20 68 6f 73 74 20 70 61 74  d-only, host pat
3cd0: 68 20 3d 3e 20 69 6e 73 69 64 65 20 74 68 65 20  h => inside the 
3ce0: 64 61 65 6d 6f 6e 0a 20 20 67 65 74 74 65 72 20  daemon.  getter 
3cf0: 72 65 61 64 5f 77 72 69 74 65 20 3d 20 5b 5d 20  read_write = [] 
3d00: 6f 66 20 53 74 72 69 6e 67 20 20 20 20 20 20 23  of String      #
3d10: 20 6d 6f 72 65 20 72 65 61 64 2d 77 72 69 74 65   more read-write
3d20: 20 68 6f 73 74 20 70 61 74 68 73 2c 20 61 74 20   host paths, at 
3d30: 74 68 65 20 73 61 6d 65 20 70 6c 61 63 65 0a 20  the same place. 
3d40: 20 67 65 74 74 65 72 20 6c 61 75 6e 63 68 65 72   getter launcher
3d50: 20 3a 20 53 74 72 69 6e 67 3f 0a 65 6e 64 0a 0a   : String?.end..
3d60: 73 74 72 75 63 74 20 53 65 72 76 65 72 0a 20 20  struct Server.  
3d70: 69 6e 63 6c 75 64 65 20 4a 53 4f 4e 3a 3a 53 65  include JSON::Se
3d80: 72 69 61 6c 69 7a 61 62 6c 65 0a 20 20 67 65 74  rializable.  get
3d90: 74 65 72 20 6f 63 78 20 3a 20 4f 43 58 3f 0a 65  ter ocx : OCX?.e
3da0: 6e 64 0a 0a 6c 69 62 20 4c 69 62 43 0a 20 20 66  nd..lib LibC.  f
3db0: 75 6e 20 65 78 65 63 76 28 70 61 74 68 20 3a 20  un execv(path : 
3dc0: 43 68 61 72 2a 2c 20 61 72 67 76 20 3a 20 43 68  Char*, argv : Ch
3dd0: 61 72 2a 2a 29 20 3a 20 49 6e 74 0a 65 6e 64 0a  ar**) : Int.end.
3de0: 0a 62 65 67 69 6e 0a 20 20 23 20 43 68 6f 6f 73  .begin.  # Choos
3df0: 65 20 74 68 65 20 6d 6f 64 65 3b 20 74 68 65 20  e the mode; the 
3e00: 43 47 49 20 75 73 65 73 20 74 68 65 20 73 61 6d  CGI uses the sam
3e10: 65 20 70 61 74 68 20 72 75 6c 65 20 61 73 20 74  e path rule as t
3e20: 68 65 20 77 65 62 20 70 61 67 65 2e 0a 20 20 6d  he web page..  m
3e30: 6f 64 65 20 3d 20 46 69 6c 65 2e 62 61 73 65 6e  ode = File.basen
3e40: 61 6d 65 28 50 52 4f 47 52 41 4d 5f 4e 41 4d 45  ame(PROGRAM_NAME
3e50: 29 20 3d 3d 20 22 6f 63 78 32 2d 63 67 69 22 20  ) == "ocx2-cgi" 
3e60: 3f 20 22 63 67 69 22 20 3a 20 41 52 47 56 2e 66  ? "cgi" : ARGV.f
3e70: 69 72 73 74 3f 0a 20 20 61 62 6f 72 74 20 22 55  irst?.  abort "U
3e80: 73 61 67 65 3a 20 73 74 61 72 74 2d 6f 63 78 20  sage: start-ocx 
3e90: 72 65 63 65 69 70 74 73 7c 64 61 65 6d 6f 6e 3b  receipts|daemon;
3ea0: 20 6f 63 78 32 2d 63 67 69 22 2c 20 32 20 75 6e   ocx2-cgi", 2 un
3eb0: 6c 65 73 73 20 28 6d 6f 64 65 20 3d 3d 20 22 63  less (mode == "c
3ec0: 67 69 22 20 26 26 20 41 52 47 56 2e 65 6d 70 74  gi" && ARGV.empt
3ed0: 79 3f 29 20 7c 7c 20 28 41 52 47 56 2e 73 69 7a  y?) || (ARGV.siz
3ee0: 65 20 3d 3d 20 31 20 26 26 20 7b 22 72 65 63 65  e == 1 && {"rece
3ef0: 69 70 74 73 22 2c 20 22 64 61 65 6d 6f 6e 22 7d  ipts", "daemon"}
3f00: 2e 69 6e 63 6c 75 64 65 73 3f 28 6d 6f 64 65 29  .includes?(mode)
3f10: 29 0a 20 20 66 69 6c 65 20 3d 20 46 69 6c 65 2e  ).  file = File.
3f20: 6a 6f 69 6e 28 46 69 6c 65 2e 64 69 72 6e 61 6d  join(File.dirnam
3f30: 65 28 50 72 6f 63 65 73 73 2e 65 78 65 63 75 74  e(Process.execut
3f40: 61 62 6c 65 5f 70 61 74 68 2e 6e 6f 74 5f 6e 69  able_path.not_ni
3f50: 6c 21 29 2c 20 22 73 65 72 76 65 72 73 2e 6a 73  l!), "servers.js
3f60: 6f 6e 22 29 0a 20 20 62 65 67 69 6e 0a 20 20 20  on").  begin.   
3f70: 20 73 65 72 76 65 72 73 20 3d 20 46 69 6c 65 2e   servers = File.
3f80: 6f 70 65 6e 28 66 69 6c 65 29 20 64 6f 20 7c 69  open(file) do |i
3f90: 6e 70 75 74 7c 0a 20 20 20 20 20 20 69 6e 66 6f  nput|.      info
3fa0: 20 3d 20 69 6e 70 75 74 2e 69 6e 66 6f 0a 20 20   = input.info.  
3fb0: 20 20 20 20 61 62 6f 72 74 20 22 23 7b 66 69 6c      abort "#{fil
3fc0: 65 7d 3a 20 6d 75 73 74 20 62 65 20 6f 77 6e 65  e}: must be owne
3fd0: 64 20 62 79 20 72 6f 6f 74 20 61 6e 64 20 6e 6f  d by root and no
3fe0: 74 20 77 72 69 74 61 62 6c 65 20 62 79 20 67 72  t writable by gr
3ff0: 6f 75 70 20 6f 72 20 6f 74 68 65 72 73 22 2c 20  oup or others", 
4000: 32 20 75 6e 6c 65 73 73 20 69 6e 66 6f 2e 6f 77  2 unless info.ow
4010: 6e 65 72 5f 69 64 20 3d 3d 20 22 30 22 20 26 26  ner_id == "0" &&
4020: 20 69 6e 66 6f 2e 70 65 72 6d 69 73 73 69 6f 6e   info.permission
4030: 73 2e 76 61 6c 75 65 20 26 20 30 6f 30 32 32 20  s.value & 0o022 
4040: 3d 3d 20 30 0a 20 20 20 20 20 20 48 61 73 68 28  == 0.      Hash(
4050: 53 74 72 69 6e 67 2c 20 4a 53 4f 4e 3a 3a 41 6e  String, JSON::An
4060: 79 29 2e 66 72 6f 6d 5f 6a 73 6f 6e 28 69 6e 70  y).from_json(inp
4070: 75 74 29 0a 20 20 20 20 65 6e 64 0a 20 20 20 20  ut).    end.    
4080: 6c 6f 63 61 6c 20 3d 20 73 65 72 76 65 72 73 5b  local = servers[
4090: 53 79 73 74 65 6d 2e 68 6f 73 74 6e 61 6d 65 5d  System.hostname]
40a0: 3f 2e 74 72 79 20 7b 20 7c 65 6e 74 72 79 7c 20  ?.try { |entry| 
40b0: 53 65 72 76 65 72 2e 66 72 6f 6d 5f 6a 73 6f 6e  Server.from_json
40c0: 28 65 6e 74 72 79 2e 74 6f 5f 6a 73 6f 6e 29 20  (entry.to_json) 
40d0: 7d 0a 20 20 20 20 64 65 66 61 75 6c 74 20 3d 20  }.    default = 
40e0: 73 65 72 76 65 72 73 5b 22 2a 22 5d 3f 2e 74 72  servers["*"]?.tr
40f0: 79 20 7b 20 7c 65 6e 74 72 79 7c 20 53 65 72 76  y { |entry| Serv
4100: 65 72 2e 66 72 6f 6d 5f 6a 73 6f 6e 28 65 6e 74  er.from_json(ent
4110: 72 79 2e 74 6f 5f 6a 73 6f 6e 29 20 7d 0a 20 20  ry.to_json) }.  
4120: 20 20 73 65 74 74 69 6e 67 73 20 3d 20 6c 6f 63    settings = loc
4130: 61 6c 2e 74 72 79 28 26 2e 6f 63 78 29 20 7c 7c  al.try(&.ocx) ||
4140: 20 64 65 66 61 75 6c 74 2e 74 72 79 28 26 2e 6f   default.try(&.o
4150: 63 78 29 20 7c 7c 20 61 62 6f 72 74 20 22 23 7b  cx) || abort "#{
4160: 66 69 6c 65 7d 3a 20 6d 69 73 73 69 6e 67 20 6f  file}: missing o
4170: 63 78 22 2c 20 32 0a 20 20 72 65 73 63 75 65 20  cx", 2.  rescue 
4180: 65 78 20 3a 20 49 4f 3a 3a 45 72 72 6f 72 20 7c  ex : IO::Error |
4190: 20 4a 53 4f 4e 3a 3a 50 61 72 73 65 45 78 63 65   JSON::ParseExce
41a0: 70 74 69 6f 6e 0a 20 20 20 20 61 62 6f 72 74 20  ption.    abort 
41b0: 22 23 7b 66 69 6c 65 7d 3a 20 63 61 6e 6e 6f 74  "#{file}: cannot
41c0: 20 72 65 61 64 20 6f 63 78 3a 20 23 7b 65 78 2e   read ocx: #{ex.
41d0: 6d 65 73 73 61 67 65 2e 74 6f 5f 73 2e 6c 69 6e  message.to_s.lin
41e0: 65 73 2e 66 69 72 73 74 7d 22 2c 20 32 0a 20 20  es.first}", 2.  
41f0: 65 6e 64 0a 20 20 6d 69 73 73 69 6e 67 20 3d 20  end.  missing = 
4200: 2d 3e 28 6e 61 6d 65 20 3a 20 53 74 72 69 6e 67  ->(name : String
4210: 29 20 7b 20 61 62 6f 72 74 20 22 23 7b 66 69 6c  ) { abort "#{fil
4220: 65 7d 3a 20 6f 63 78 20 68 61 73 20 6e 6f 20 23  e}: ocx has no #
4230: 7b 6e 61 6d 65 7d 20 66 6f 72 20 23 7b 6d 6f 64  {name} for #{mod
4240: 65 7d 22 2c 20 32 20 7d 0a 20 20 63 61 73 65 20  e}", 2 }.  case 
4250: 6d 6f 64 65 0a 20 20 77 68 65 6e 20 22 63 67 69  mode.  when "cgi
4260: 22 0a 20 20 20 20 72 65 6c 65 61 73 65 73 2c 20  ".    releases, 
4270: 73 6f 63 6b 65 74 5f 64 69 72 20 3d 20 73 65 74  socket_dir = set
4280: 74 69 6e 67 73 2e 72 65 6c 65 61 73 65 73 20 7c  tings.releases |
4290: 7c 20 6d 69 73 73 69 6e 67 2e 63 61 6c 6c 28 22  | missing.call("
42a0: 72 65 6c 65 61 73 65 73 22 29 2c 20 73 65 74 74  releases"), sett
42b0: 69 6e 67 73 2e 73 6f 63 6b 65 74 5f 64 69 72 20  ings.socket_dir 
42c0: 7c 7c 20 6d 69 73 73 69 6e 67 2e 63 61 6c 6c 28  || missing.call(
42d0: 22 73 6f 63 6b 65 74 5f 64 69 72 22 29 0a 20 20  "socket_dir").  
42e0: 20 20 72 65 6c 65 61 73 65 20 3d 20 46 69 6c 65    release = File
42f0: 2e 72 65 61 6c 70 61 74 68 28 46 69 6c 65 2e 6a  .realpath(File.j
4300: 6f 69 6e 28 72 65 6c 65 61 73 65 73 2c 20 22 63  oin(releases, "c
4310: 75 72 72 65 6e 74 22 29 29 0a 20 20 20 20 77 6f  urrent")).    wo
4320: 72 64 73 20 3d 20 28 45 4e 56 5b 22 50 41 54 48  rds = (ENV["PATH
4330: 5f 49 4e 46 4f 22 5d 3f 20 7c 7c 20 22 22 29 2e  _INFO"]? || "").
4340: 73 70 6c 69 74 28 27 2f 27 29 2e 72 65 6a 65 63  split('/').rejec
4350: 74 28 26 2e 65 6d 70 74 79 3f 29 0a 20 20 20 20  t(&.empty?).    
4360: 6e 61 6d 65 20 3d 20 77 6f 72 64 73 5b 28 28 77  name = words[((w
4370: 6f 72 64 73 2e 72 69 6e 64 65 78 28 22 6f 63 78  ords.rindex("ocx
4380: 32 2d 63 67 69 22 29 20 7c 7c 20 2d 31 29 20 2b  2-cgi") || -1) +
4390: 20 31 29 2e 2e 5d 2e 66 69 72 73 74 3f 0a 20 20   1)..].first?.  
43a0: 20 20 75 6e 6c 65 73 73 20 6e 61 6d 65 20 3d 3d    unless name ==
43b0: 20 22 72 65 63 65 69 70 74 22 0a 20 20 20 20 20   "receipt".     
43c0: 20 23 20 54 65 6d 70 6f 72 61 72 79 20 70 61 73   # Temporary pas
43d0: 73 74 68 72 6f 75 67 68 20 75 6e 74 69 6c 20 74  sthrough until t
43e0: 68 65 20 6f 74 68 65 72 20 43 47 49 20 74 6f 6f  he other CGI too
43f0: 6c 73 20 74 61 6c 6b 20 74 6f 20 74 68 65 20 64  ls talk to the d
4400: 61 65 6d 6f 6e 2e 0a 20 20 20 20 20 20 61 72 67  aemon..      arg
4410: 76 20 3d 20 5b 46 69 6c 65 2e 6a 6f 69 6e 28 72  v = [File.join(r
4420: 65 6c 65 61 73 65 2c 20 22 6f 63 78 32 2d 63 67  elease, "ocx2-cg
4430: 69 22 29 2e 74 6f 5f 75 6e 73 61 66 65 2c 20 50  i").to_unsafe, P
4440: 6f 69 6e 74 65 72 28 55 49 6e 74 38 29 2e 6e 75  ointer(UInt8).nu
4450: 6c 6c 5d 0a 20 20 20 20 20 20 4c 69 62 43 2e 65  ll].      LibC.e
4460: 78 65 63 76 28 46 69 6c 65 2e 6a 6f 69 6e 28 72  xecv(File.join(r
4470: 65 6c 65 61 73 65 2c 20 22 6f 63 78 32 22 29 2c  elease, "ocx2"),
4480: 20 61 72 67 76 2e 74 6f 5f 75 6e 73 61 66 65 29   argv.to_unsafe)
4490: 0a 20 20 20 20 20 20 72 61 69 73 65 20 49 4f 3a  .      raise IO:
44a0: 3a 45 72 72 6f 72 2e 66 72 6f 6d 5f 65 72 72 6e  :Error.from_errn
44b0: 6f 28 22 65 78 65 63 76 22 29 0a 20 20 20 20 65  o("execv").    e
44c0: 6e 64 0a 20 20 20 20 23 20 43 6f 6e 6e 65 63 74  nd.    # Connect
44d0: 20 61 6e 64 20 77 72 69 74 65 20 46 6f 73 73 69   and write Fossi
44e0: 6c 27 73 20 69 64 65 6e 74 69 74 79 20 62 65 66  l's identity bef
44f0: 6f 72 65 20 63 6c 6f 73 69 6e 67 20 72 75 6e 74  ore closing runt
4500: 69 6d 65 20 64 65 73 63 72 69 70 74 6f 72 73 2e  ime descriptors.
4510: 0a 20 20 20 20 72 65 70 6f 73 69 74 6f 72 79 20  .    repository 
4520: 3d 20 46 69 6c 65 2e 62 61 73 65 6e 61 6d 65 28  = File.basename(
4530: 45 4e 56 5b 22 46 4f 53 53 49 4c 5f 52 45 50 4f  ENV["FOSSIL_REPO
4540: 53 49 54 4f 52 59 22 5d 3f 20 7c 7c 20 22 22 2c  SITORY"]? || "",
4550: 20 22 2e 66 6f 73 73 69 6c 22 29 0a 20 20 20 20   ".fossil").    
4560: 62 65 67 69 6e 0a 20 20 20 20 20 20 73 6f 63 6b  begin.      sock
4570: 65 74 20 3d 20 53 6f 63 6b 65 74 2e 6e 65 77 28  et = Socket.new(
4580: 3a 75 6e 69 78 2c 20 3a 73 74 72 65 61 6d 29 0a  :unix, :stream).
4590: 20 20 20 20 20 20 73 6f 63 6b 65 74 2e 63 6f 6e        socket.con
45a0: 6e 65 63 74 28 53 6f 63 6b 65 74 3a 3a 55 4e 49  nect(Socket::UNI
45b0: 58 41 64 64 72 65 73 73 2e 6e 65 77 28 46 69 6c  XAddress.new(Fil
45c0: 65 2e 6a 6f 69 6e 28 73 6f 63 6b 65 74 5f 64 69  e.join(socket_di
45d0: 72 2c 20 22 72 65 63 65 69 70 74 73 2e 73 6f 63  r, "receipts.soc
45e0: 6b 22 29 29 2c 20 32 2e 73 65 63 6f 6e 64 73 29  k")), 2.seconds)
45f0: 0a 20 20 20 20 20 20 73 6f 63 6b 65 74 2e 77 72  .      socket.wr
4600: 69 74 65 5f 74 69 6d 65 6f 75 74 20 3d 20 32 2e  ite_timeout = 2.
4610: 73 65 63 6f 6e 64 73 0a 20 20 20 20 20 20 73 6f  seconds.      so
4620: 63 6b 65 74 2e 70 75 74 73 28 7b 75 73 65 72 3a  cket.puts({user:
4630: 20 45 4e 56 5b 22 46 4f 53 53 49 4c 5f 55 53 45   ENV["FOSSIL_USE
4640: 52 22 5d 3f 2c 20 63 61 70 61 62 69 6c 69 74 69  R"]?, capabiliti
4650: 65 73 3a 20 45 4e 56 5b 22 46 4f 53 53 49 4c 5f  es: ENV["FOSSIL_
4660: 43 41 50 41 42 49 4c 49 54 49 45 53 22 5d 3f 20  CAPABILITIES"]? 
4670: 7c 7c 20 22 22 2c 20 72 65 70 6f 73 69 74 6f 72  || "", repositor
4680: 79 3a 20 72 65 70 6f 73 69 74 6f 72 79 7d 2e 74  y: repository}.t
4690: 6f 5f 6a 73 6f 6e 29 0a 20 20 20 20 20 20 73 6f  o_json).      so
46a0: 63 6b 65 74 2e 66 6c 75 73 68 0a 20 20 20 20 72  cket.flush.    r
46b0: 65 73 63 75 65 20 53 6f 63 6b 65 74 3a 3a 45 72  escue Socket::Er
46c0: 72 6f 72 20 7c 20 49 4f 3a 3a 45 72 72 6f 72 0a  ror | IO::Error.
46d0: 20 20 20 20 20 20 70 75 74 73 20 22 53 74 61 74        puts "Stat
46e0: 75 73 3a 20 35 30 33 5c 72 5c 6e 43 6f 6e 74 65  us: 503\r\nConte
46f0: 6e 74 2d 54 79 70 65 3a 20 61 70 70 6c 69 63 61  nt-Type: applica
4700: 74 69 6f 6e 2f 6a 73 6f 6e 5c 72 5c 6e 5c 72 5c  tion/json\r\n\r\
4710: 6e 7b 5c 22 75 6e 61 76 61 69 6c 61 62 6c 65 5c  n{\"unavailable\
4720: 22 3a 5c 22 72 65 63 65 69 70 74 20 73 65 72 76  ":\"receipt serv
4730: 69 63 65 20 6e 6f 74 20 61 6e 73 77 65 72 69 6e  ice not answerin
4740: 67 5c 22 7d 22 0a 20 20 20 20 20 20 65 78 69 74  g\"}".      exit
4750: 20 30 0a 20 20 20 20 65 6e 64 0a 20 20 20 20 23   0.    end.    #
4760: 20 42 75 69 6c 64 20 74 68 65 20 72 65 63 65 69   Build the recei
4770: 70 74 20 43 47 49 27 73 20 73 61 6e 64 62 6f 78  pt CGI's sandbox
4780: 20 77 69 74 68 20 6f 6e 6c 79 20 74 68 65 20 72   with only the r
4790: 65 71 75 65 73 74 20 66 69 65 6c 64 73 20 69 74  equest fields it
47a0: 20 6e 65 65 64 73 2e 0a 20 20 20 20 61 72 67 73   needs..    args
47b0: 20 3d 20 25 77 28 2d 2d 75 6e 73 68 61 72 65 2d   = %w(--unshare-
47c0: 61 6c 6c 20 2d 2d 64 69 65 2d 77 69 74 68 2d 70  all --die-with-p
47d0: 61 72 65 6e 74 20 2d 2d 6e 65 77 2d 73 65 73 73  arent --new-sess
47e0: 69 6f 6e 20 2d 2d 63 61 70 2d 64 72 6f 70 20 41  ion --cap-drop A
47f0: 4c 4c 20 2d 2d 63 6c 65 61 72 65 6e 76 20 2d 2d  LL --clearenv --
4800: 72 6f 2d 62 69 6e 64 20 2f 75 73 72 20 2f 75 73  ro-bind /usr /us
4810: 72 29 0a 20 20 20 20 25 77 28 2f 6c 69 62 20 2f  r).    %w(/lib /
4820: 6c 69 62 36 34 20 2f 62 69 6e 20 2f 65 74 63 2f  lib64 /bin /etc/
4830: 70 61 73 73 77 64 29 2e 65 61 63 68 20 7b 20 7c  passwd).each { |
4840: 70 61 74 68 7c 20 61 72 67 73 2e 63 6f 6e 63 61  path| args.conca
4850: 74 28 5b 22 2d 2d 72 6f 2d 62 69 6e 64 22 2c 20  t(["--ro-bind", 
4860: 70 61 74 68 2c 20 70 61 74 68 5d 29 20 69 66 20  path, path]) if 
4870: 46 69 6c 65 2e 65 78 69 73 74 73 3f 28 70 61 74  File.exists?(pat
4880: 68 29 20 7d 0a 20 20 20 20 61 72 67 73 2e 63 6f  h) }.    args.co
4890: 6e 63 61 74 28 5b 22 2d 2d 70 72 6f 63 22 2c 20  ncat(["--proc", 
48a0: 22 2f 70 72 6f 63 22 2c 20 22 2d 2d 64 65 76 22  "/proc", "--dev"
48b0: 2c 20 22 2f 64 65 76 22 2c 20 22 2d 2d 74 6d 70  , "/dev", "--tmp
48c0: 66 73 22 2c 20 22 2f 74 6d 70 22 2c 20 22 2d 2d  fs", "/tmp", "--
48d0: 72 6f 2d 62 69 6e 64 22 2c 20 46 69 6c 65 2e 6a  ro-bind", File.j
48e0: 6f 69 6e 28 72 65 6c 65 61 73 65 2c 20 22 6f 63  oin(release, "oc
48f0: 78 32 22 29 2c 20 22 2f 72 65 6c 65 61 73 65 2f  x2"), "/release/
4900: 6f 63 78 32 2d 63 67 69 22 5d 29 0a 20 20 20 20  ocx2-cgi"]).    
4910: 25 77 28 52 45 51 55 45 53 54 5f 4d 45 54 48 4f  %w(REQUEST_METHO
4920: 44 20 51 55 45 52 59 5f 53 54 52 49 4e 47 20 50  D QUERY_STRING P
4930: 41 54 48 5f 49 4e 46 4f 20 43 4f 4e 54 45 4e 54  ATH_INFO CONTENT
4940: 5f 4c 45 4e 47 54 48 20 43 4f 4e 54 45 4e 54 5f  _LENGTH CONTENT_
4950: 54 59 50 45 20 48 54 54 50 53 20 48 54 54 50 5f  TYPE HTTPS HTTP_
4960: 48 4f 53 54 20 48 54 54 50 5f 52 45 46 45 52 45  HOST HTTP_REFERE
4970: 52 20 46 4f 53 53 49 4c 5f 55 53 45 52 20 46 4f  R FOSSIL_USER FO
4980: 53 53 49 4c 5f 43 41 50 41 42 49 4c 49 54 49 45  SSIL_CAPABILITIE
4990: 53 20 46 4f 53 53 49 4c 5f 55 52 49 29 2e 65 61  S FOSSIL_URI).ea
49a0: 63 68 20 64 6f 20 7c 6b 65 79 7c 0a 20 20 20 20  ch do |key|.    
49b0: 20 20 61 72 67 73 2e 63 6f 6e 63 61 74 28 5b 22    args.concat(["
49c0: 2d 2d 73 65 74 65 6e 76 22 2c 20 6b 65 79 2c 20  --setenv", key, 
49d0: 45 4e 56 5b 6b 65 79 5d 5d 29 20 69 66 20 45 4e  ENV[key]]) if EN
49e0: 56 2e 68 61 73 5f 6b 65 79 3f 28 6b 65 79 29 0a  V.has_key?(key).
49f0: 20 20 20 20 65 6e 64 0a 20 20 20 20 61 72 67 73      end.    args
4a00: 2e 63 6f 6e 63 61 74 28 5b 22 2d 2d 73 65 74 65  .concat(["--sete
4a10: 6e 76 22 2c 20 22 46 4f 53 53 49 4c 5f 52 45 50  nv", "FOSSIL_REP
4a20: 4f 53 49 54 4f 52 59 22 2c 20 72 65 70 6f 73 69  OSITORY", reposi
4a30: 74 6f 72 79 2c 20 22 2d 2d 73 65 74 65 6e 76 22  tory, "--setenv"
4a40: 2c 20 22 50 41 54 48 22 2c 20 22 2f 75 73 72 2f  , "PATH", "/usr/
4a50: 62 69 6e 22 2c 20 22 2f 72 65 6c 65 61 73 65 2f  bin", "/release/
4a60: 6f 63 78 32 2d 63 67 69 22 2c 20 22 2d 2d 72 65  ocx2-cgi", "--re
4a70: 63 65 69 70 74 2d 66 64 22 2c 20 22 33 22 5d 29  ceipt-fd", "3"])
4a80: 0a 20 20 20 20 23 20 4b 65 65 70 20 6f 6e 6c 79  .    # Keep only
4a90: 20 74 68 65 20 73 6f 63 6b 65 74 20 61 62 6f 76   the socket abov
4aa0: 65 20 73 74 64 65 72 72 2c 20 61 74 20 64 65 73  e stderr, at des
4ab0: 63 72 69 70 74 6f 72 20 33 2c 20 74 68 72 6f 75  criptor 3, throu
4ac0: 67 68 20 65 78 65 63 2e 0a 20 20 20 20 64 65 73  gh exec..    des
4ad0: 63 72 69 70 74 6f 72 73 20 3d 20 44 69 72 2e 63  criptors = Dir.c
4ae0: 68 69 6c 64 72 65 6e 28 22 2f 70 72 6f 63 2f 73  hildren("/proc/s
4af0: 65 6c 66 2f 66 64 22 29 2e 6d 61 70 28 26 2e 74  elf/fd").map(&.t
4b00: 6f 5f 69 29 0a 20 20 20 20 64 65 73 63 72 69 70  o_i).    descrip
4b10: 74 6f 72 73 2e 65 61 63 68 20 7b 20 7c 66 64 7c  tors.each { |fd|
4b20: 20 4c 69 62 43 2e 63 6c 6f 73 65 28 66 64 29 20   LibC.close(fd) 
4b30: 69 66 20 66 64 20 3e 20 32 20 26 26 20 66 64 20  if fd > 2 && fd 
4b40: 21 3d 20 73 6f 63 6b 65 74 2e 66 64 20 7d 0a 20  != socket.fd }. 
4b50: 20 20 20 72 61 69 73 65 20 49 4f 3a 3a 45 72 72     raise IO::Err
4b60: 6f 72 2e 66 72 6f 6d 5f 65 72 72 6e 6f 28 22 64  or.from_errno("d
4b70: 75 70 32 22 29 20 69 66 20 4c 69 62 43 2e 64 75  up2") if LibC.du
4b80: 70 32 28 73 6f 63 6b 65 74 2e 66 64 2c 20 33 29  p2(socket.fd, 3)
4b90: 20 3d 3d 20 2d 31 0a 20 20 20 20 4c 69 62 43 2e   == -1.    LibC.
4ba0: 63 6c 6f 73 65 28 73 6f 63 6b 65 74 2e 66 64 29  close(socket.fd)
4bb0: 20 75 6e 6c 65 73 73 20 73 6f 63 6b 65 74 2e 66   unless socket.f
4bc0: 64 20 3d 3d 20 33 0a 20 20 20 20 72 61 69 73 65  d == 3.    raise
4bd0: 20 49 4f 3a 3a 45 72 72 6f 72 2e 66 72 6f 6d 5f   IO::Error.from_
4be0: 65 72 72 6e 6f 28 22 66 63 6e 74 6c 22 29 20 69  errno("fcntl") i
4bf0: 66 20 4c 69 62 43 2e 66 63 6e 74 6c 28 33 2c 20  f LibC.fcntl(3, 
4c00: 4c 69 62 43 3a 3a 46 5f 53 45 54 46 44 2c 20 30  LibC::F_SETFD, 0
4c10: 29 20 3d 3d 20 2d 31 0a 20 20 77 68 65 6e 20 22  ) == -1.  when "
4c20: 72 65 63 65 69 70 74 73 22 0a 20 20 20 20 23 20  receipts".    # 
4c30: 47 69 76 65 20 74 68 65 20 73 65 72 76 69 63 65  Give the service
4c40: 20 6f 6e 6c 79 20 74 68 65 20 72 65 70 6f 73 69   only the reposi
4c50: 74 6f 72 79 20 64 69 72 65 63 74 6f 72 79 20 61  tory directory a
4c60: 6e 64 20 69 74 73 20 73 6f 63 6b 65 74 20 64 69  nd its socket di
4c70: 72 65 63 74 6f 72 79 2e 0a 20 20 20 20 72 65 70  rectory..    rep
4c80: 6f 73 69 74 6f 72 69 65 73 2c 20 73 6f 63 6b 65  ositories, socke
4c90: 74 5f 64 69 72 20 3d 20 73 65 74 74 69 6e 67 73  t_dir = settings
4ca0: 2e 72 65 70 6f 73 69 74 6f 72 69 65 73 20 7c 7c  .repositories ||
4cb0: 20 6d 69 73 73 69 6e 67 2e 63 61 6c 6c 28 22 72   missing.call("r
4cc0: 65 70 6f 73 69 74 6f 72 69 65 73 22 29 2c 20 73  epositories"), s
4cd0: 65 74 74 69 6e 67 73 2e 73 6f 63 6b 65 74 5f 64  ettings.socket_d
4ce0: 69 72 20 7c 7c 20 6d 69 73 73 69 6e 67 2e 63 61  ir || missing.ca
4cf0: 6c 6c 28 22 73 6f 63 6b 65 74 5f 64 69 72 22 29  ll("socket_dir")
4d00: 0a 20 20 20 20 65 78 65 63 75 74 61 62 6c 65 20  .    executable 
4d10: 3d 20 50 72 6f 63 65 73 73 2e 65 78 65 63 75 74  = Process.execut
4d20: 61 62 6c 65 5f 70 61 74 68 20 7c 7c 20 72 61 69  able_path || rai
4d30: 73 65 20 49 4f 3a 3a 45 72 72 6f 72 2e 6e 65 77  se IO::Error.new
4d40: 28 22 63 61 6e 6e 6f 74 20 6c 6f 63 61 74 65 20  ("cannot locate 
4d50: 65 78 65 63 75 74 61 62 6c 65 22 29 0a 20 20 20  executable").   
4d60: 20 61 72 67 73 20 3d 20 25 77 28 2d 2d 75 6e 73   args = %w(--uns
4d70: 68 61 72 65 2d 61 6c 6c 20 2d 2d 64 69 65 2d 77  hare-all --die-w
4d80: 69 74 68 2d 70 61 72 65 6e 74 20 2d 2d 6e 65 77  ith-parent --new
4d90: 2d 73 65 73 73 69 6f 6e 20 2d 2d 63 61 70 2d 64  -session --cap-d
4da0: 72 6f 70 20 41 4c 4c 20 2d 2d 63 6c 65 61 72 65  rop ALL --cleare
4db0: 6e 76 20 2d 2d 72 6f 2d 62 69 6e 64 20 2f 75 73  nv --ro-bind /us
4dc0: 72 20 2f 75 73 72 29 0a 20 20 20 20 25 77 28 2f  r /usr).    %w(/
4dd0: 6c 69 62 20 2f 6c 69 62 36 34 29 2e 65 61 63 68  lib /lib64).each
4de0: 20 7b 20 7c 70 61 74 68 7c 20 61 72 67 73 2e 63   { |path| args.c
4df0: 6f 6e 63 61 74 28 5b 22 2d 2d 72 6f 2d 62 69 6e  oncat(["--ro-bin
4e00: 64 22 2c 20 70 61 74 68 2c 20 70 61 74 68 5d 29  d", path, path])
4e10: 20 69 66 20 46 69 6c 65 2e 65 78 69 73 74 73 3f   if File.exists?
4e20: 28 70 61 74 68 29 20 7d 0a 20 20 20 20 61 72 67  (path) }.    arg
4e30: 73 2e 63 6f 6e 63 61 74 28 5b 22 2d 2d 64 65 76  s.concat(["--dev
4e40: 22 2c 20 22 2f 64 65 76 22 2c 20 22 2d 2d 74 6d  ", "/dev", "--tm
4e50: 70 66 73 22 2c 20 22 2f 74 6d 70 22 2c 20 22 2d  pfs", "/tmp", "-
4e60: 2d 72 6f 2d 62 69 6e 64 22 2c 20 72 65 70 6f 73  -ro-bind", repos
4e70: 69 74 6f 72 69 65 73 2c 20 22 2f 72 65 70 6f 73  itories, "/repos
4e80: 69 74 6f 72 69 65 73 22 2c 20 22 2d 2d 62 69 6e  itories", "--bin
4e90: 64 22 2c 20 73 6f 63 6b 65 74 5f 64 69 72 2c 20  d", socket_dir, 
4ea0: 22 2f 72 75 6e 2f 6f 63 78 22 2c 0a 20 20 20 20  "/run/ocx",.    
4eb0: 20 20 20 20 20 20 20 20 20 20 20 20 20 22 2d 2d               "--
4ec0: 72 6f 2d 62 69 6e 64 22 2c 20 46 69 6c 65 2e 6a  ro-bind", File.j
4ed0: 6f 69 6e 28 46 69 6c 65 2e 64 69 72 6e 61 6d 65  oin(File.dirname
4ee0: 28 65 78 65 63 75 74 61 62 6c 65 29 2c 20 22 72  (executable), "r
4ef0: 65 63 65 69 70 74 73 22 29 2c 20 22 2f 72 65 63  eceipts"), "/rec
4f00: 65 69 70 74 73 22 2c 20 22 2f 72 65 63 65 69 70  eipts", "/receip
4f10: 74 73 22 5d 29 0a 20 20 77 68 65 6e 20 22 64 61  ts"]).  when "da
4f20: 65 6d 6f 6e 22 0a 20 20 20 20 23 20 53 68 61 72  emon".    # Shar
4f30: 65 20 70 72 6f 63 65 73 73 65 73 20 61 6e 64 20  e processes and 
4f40: 6e 65 74 77 6f 72 6b 20 73 6f 20 77 61 74 63 68  network so watch
4f50: 65 72 73 20 63 61 6e 20 6f 75 74 6c 69 76 65 20  ers can outlive 
4f60: 61 20 64 61 65 6d 6f 6e 20 72 65 73 74 61 72 74  a daemon restart
4f70: 2e 0a 20 20 20 20 72 6f 6f 74 20 3d 20 73 65 74  ..    root = set
4f80: 74 69 6e 67 73 2e 64 61 65 6d 6f 6e 5f 72 6f 6f  tings.daemon_roo
4f90: 74 20 7c 7c 20 6d 69 73 73 69 6e 67 2e 63 61 6c  t || missing.cal
4fa0: 6c 28 22 64 61 65 6d 6f 6e 5f 72 6f 6f 74 22 29  l("daemon_root")
4fb0: 0a 20 20 20 20 61 72 67 73 20 3d 20 25 77 28 2d  .    args = %w(-
4fc0: 2d 75 6e 73 68 61 72 65 2d 75 73 65 72 20 2d 2d  -unshare-user --
4fd0: 75 6e 73 68 61 72 65 2d 69 70 63 20 2d 2d 75 6e  unshare-ipc --un
4fe0: 73 68 61 72 65 2d 75 74 73 20 2d 2d 63 6c 65 61  share-uts --clea
4ff0: 72 65 6e 76 20 2d 2d 73 65 74 65 6e 76 20 48 4f  renv --setenv HO
5000: 4d 45 20 2f 72 6f 6f 74 20 2d 2d 73 65 74 65 6e  ME /root --seten
5010: 76 20 50 41 54 48 20 2f 75 73 72 2f 62 69 6e 3a  v PATH /usr/bin:
5020: 2f 62 69 6e 20 2d 2d 72 6f 2d 62 69 6e 64 20 2f  /bin --ro-bind /
5030: 75 73 72 20 2f 75 73 72 29 0a 20 20 20 20 25 77  usr /usr).    %w
5040: 28 2f 6c 69 62 20 2f 6c 69 62 36 34 20 2f 62 69  (/lib /lib64 /bi
5050: 6e 20 2f 73 62 69 6e 20 2f 65 74 63 2f 73 73 6c  n /sbin /etc/ssl
5060: 20 2f 65 74 63 2f 70 6b 69 20 2f 65 74 63 2f 72   /etc/pki /etc/r
5070: 65 73 6f 6c 76 2e 63 6f 6e 66 20 2f 65 74 63 2f  esolv.conf /etc/
5080: 68 6f 73 74 73 20 2f 65 74 63 2f 6e 73 73 77 69  hosts /etc/nsswi
5090: 74 63 68 2e 63 6f 6e 66 20 2f 65 74 63 2f 70 61  tch.conf /etc/pa
50a0: 73 73 77 64 20 2f 65 74 63 2f 67 72 6f 75 70 29  sswd /etc/group)
50b0: 2e 65 61 63 68 20 64 6f 20 7c 70 61 74 68 7c 0a  .each do |path|.
50c0: 20 20 20 20 20 20 61 72 67 73 2e 63 6f 6e 63 61        args.conca
50d0: 74 28 5b 22 2d 2d 72 6f 2d 62 69 6e 64 22 2c 20  t(["--ro-bind", 
50e0: 70 61 74 68 2c 20 70 61 74 68 5d 29 20 69 66 20  path, path]) if 
50f0: 46 69 6c 65 2e 65 78 69 73 74 73 3f 28 70 61 74  File.exists?(pat
5100: 68 29 0a 20 20 20 20 65 6e 64 0a 20 20 20 20 61  h).    end.    a
5110: 72 67 73 2e 63 6f 6e 63 61 74 28 25 77 28 2d 2d  rgs.concat(%w(--
5120: 70 72 6f 63 20 2f 70 72 6f 63 20 2d 2d 64 65 76  proc /proc --dev
5130: 20 2f 64 65 76 20 2d 2d 74 6d 70 66 73 20 2f 74   /dev --tmpfs /t
5140: 6d 70 20 2d 2d 74 6d 70 66 73 20 2f 72 6f 6f 74  mp --tmpfs /root
5150: 29 29 0a 20 20 20 20 73 65 74 74 69 6e 67 73 2e  )).    settings.
5160: 68 6f 6d 65 73 2e 65 61 63 68 20 7b 20 7c 68 6f  homes.each { |ho
5170: 73 74 2c 20 69 6e 73 69 64 65 7c 20 61 72 67 73  st, inside| args
5180: 2e 63 6f 6e 63 61 74 28 5b 22 2d 2d 62 69 6e 64  .concat(["--bind
5190: 22 2c 20 68 6f 73 74 2c 20 69 6e 73 69 64 65 5d  ", host, inside]
51a0: 29 20 69 66 20 46 69 6c 65 2e 65 78 69 73 74 73  ) if File.exists
51b0: 3f 28 68 6f 73 74 29 20 7d 0a 20 20 20 20 73 65  ?(host) }.    se
51c0: 74 74 69 6e 67 73 2e 74 6f 6f 6c 73 2e 65 61 63  ttings.tools.eac
51d0: 68 20 7b 20 7c 68 6f 73 74 2c 20 69 6e 73 69 64  h { |host, insid
51e0: 65 7c 20 61 72 67 73 2e 63 6f 6e 63 61 74 28 5b  e| args.concat([
51f0: 22 2d 2d 72 6f 2d 62 69 6e 64 22 2c 20 68 6f 73  "--ro-bind", hos
5200: 74 2c 20 69 6e 73 69 64 65 5d 29 20 69 66 20 46  t, inside]) if F
5210: 69 6c 65 2e 65 78 69 73 74 73 3f 28 68 6f 73 74  ile.exists?(host
5220: 29 20 7d 0a 20 20 20 20 73 65 74 74 69 6e 67 73  ) }.    settings
5230: 2e 72 65 61 64 5f 77 72 69 74 65 2e 65 61 63 68  .read_write.each
5240: 20 7b 20 7c 70 61 74 68 7c 20 61 72 67 73 2e 63   { |path| args.c
5250: 6f 6e 63 61 74 28 5b 22 2d 2d 62 69 6e 64 22 2c  oncat(["--bind",
5260: 20 70 61 74 68 2c 20 70 61 74 68 5d 29 20 7d 0a   path, path]) }.
5270: 20 20 20 20 61 72 67 73 2e 63 6f 6e 63 61 74 28      args.concat(
5280: 5b 22 2d 2d 62 69 6e 64 22 2c 20 72 6f 6f 74 2c  ["--bind", root,
5290: 20 72 6f 6f 74 2c 20 46 69 6c 65 2e 6a 6f 69 6e   root, File.join
52a0: 28 72 6f 6f 74 2c 20 22 63 75 72 72 65 6e 74 2f  (root, "current/
52b0: 6f 63 78 32 22 29 2c 20 22 64 61 65 6d 6f 6e 22  ocx2"), "daemon"
52c0: 2c 20 46 69 6c 65 2e 6a 6f 69 6e 28 72 6f 6f 74  , File.join(root
52d0: 2c 20 22 6e 6f 64 65 2f 6e 6f 64 65 73 2e 6a 73  , "node/nodes.js
52e0: 6f 6e 22 29 5d 29 0a 20 20 20 20 23 20 52 65 66  on")]).    # Ref
52f0: 75 73 65 20 61 6e 79 20 62 69 6e 64 20 74 68 61  use any bind tha
5300: 74 20 6f 76 65 72 6c 61 70 73 20 74 68 65 20 73  t overlaps the s
5310: 65 72 76 65 64 20 72 65 70 6f 73 69 74 6f 72 69  erved repositori
5320: 65 73 2c 20 66 6f 6c 6c 6f 77 69 6e 67 20 6c 69  es, following li
5330: 6e 6b 73 2e 0a 20 20 20 20 70 61 74 68 73 20 3d  nks..    paths =
5340: 20 5b 73 65 74 74 69 6e 67 73 2e 72 65 70 6f 73   [settings.repos
5350: 69 74 6f 72 69 65 73 20 7c 7c 20 22 2f 6e 6f 6e  itories || "/non
5360: 65 78 69 73 74 65 6e 74 2d 72 65 70 6f 73 69 74  existent-reposit
5370: 6f 72 69 65 73 22 5d 20 2b 20 61 72 67 73 2e 65  ories"] + args.e
5380: 61 63 68 5f 69 6e 64 65 78 2e 73 65 6c 65 63 74  ach_index.select
5390: 20 7b 20 7c 69 7c 20 7b 22 2d 2d 62 69 6e 64 22   { |i| {"--bind"
53a0: 2c 20 22 2d 2d 72 6f 2d 62 69 6e 64 22 7d 2e 69  , "--ro-bind"}.i
53b0: 6e 63 6c 75 64 65 73 3f 28 61 72 67 73 5b 69 5d  ncludes?(args[i]
53c0: 29 20 7d 2e 6d 61 70 20 7b 20 7c 69 7c 20 61 72  ) }.map { |i| ar
53d0: 67 73 5b 69 20 2b 20 31 5d 20 7d 2e 74 6f 5f 61  gs[i + 1] }.to_a
53e0: 0a 20 20 20 20 70 61 74 68 73 2e 6d 61 70 21 20  .    paths.map! 
53f0: 64 6f 20 7c 70 61 74 68 7c 0a 20 20 20 20 20 20  do |path|.      
5400: 6d 69 73 73 69 6e 67 20 3d 20 5b 5d 20 6f 66 20  missing = [] of 
5410: 53 74 72 69 6e 67 0a 20 20 20 20 20 20 70 61 74  String.      pat
5420: 68 20 3d 20 46 69 6c 65 2e 65 78 70 61 6e 64 5f  h = File.expand_
5430: 70 61 74 68 28 70 61 74 68 29 0a 20 20 20 20 20  path(path).     
5440: 20 75 6e 74 69 6c 20 46 69 6c 65 2e 65 78 69 73   until File.exis
5450: 74 73 3f 28 70 61 74 68 29 20 7c 7c 20 46 69 6c  ts?(path) || Fil
5460: 65 2e 73 79 6d 6c 69 6e 6b 3f 28 70 61 74 68 29  e.symlink?(path)
5470: 0a 20 20 20 20 20 20 20 20 6d 69 73 73 69 6e 67  .        missing
5480: 2e 75 6e 73 68 69 66 74 28 46 69 6c 65 2e 62 61  .unshift(File.ba
5490: 73 65 6e 61 6d 65 28 70 61 74 68 29 29 0a 20 20  sename(path)).  
54a0: 20 20 20 20 20 20 70 61 74 68 20 3d 20 46 69 6c        path = Fil
54b0: 65 2e 64 69 72 6e 61 6d 65 28 70 61 74 68 29 0a  e.dirname(path).
54c0: 20 20 20 20 20 20 65 6e 64 0a 20 20 20 20 20 20        end.      
54d0: 46 69 6c 65 2e 6a 6f 69 6e 28 5b 46 69 6c 65 2e  File.join([File.
54e0: 72 65 61 6c 70 61 74 68 28 70 61 74 68 29 5d 20  realpath(path)] 
54f0: 2b 20 6d 69 73 73 69 6e 67 29 2e 72 63 68 6f 70  + missing).rchop
5500: 28 27 2f 27 29 0a 20 20 20 20 65 6e 64 0a 20 20  ('/').    end.  
5510: 20 20 72 65 70 6f 73 69 74 6f 72 79 20 3d 20 70    repository = p
5520: 61 74 68 73 2e 73 68 69 66 74 0a 20 20 20 20 70  aths.shift.    p
5530: 61 74 68 73 2e 65 61 63 68 20 64 6f 20 7c 70 61  aths.each do |pa
5540: 74 68 7c 0a 20 20 20 20 20 20 61 62 6f 72 74 20  th|.      abort 
5550: 22 64 61 65 6d 6f 6e 20 62 6f 75 6e 64 20 70 61  "daemon bound pa
5560: 74 68 20 6f 76 65 72 6c 61 70 73 20 73 65 72 76  th overlaps serv
5570: 65 64 20 72 65 70 6f 73 69 74 6f 72 69 65 73 22  ed repositories"
5580: 2c 20 32 20 69 66 20 70 61 74 68 20 3d 3d 20 72  , 2 if path == r
5590: 65 70 6f 73 69 74 6f 72 79 20 7c 7c 20 70 61 74  epository || pat
55a0: 68 2e 73 74 61 72 74 73 5f 77 69 74 68 3f 28 72  h.starts_with?(r
55b0: 65 70 6f 73 69 74 6f 72 79 20 2b 20 22 2f 22 29  epository + "/")
55c0: 20 7c 7c 20 72 65 70 6f 73 69 74 6f 72 79 2e 73   || repository.s
55d0: 74 61 72 74 73 5f 77 69 74 68 3f 28 70 61 74 68  tarts_with?(path
55e0: 20 2b 20 22 2f 22 29 0a 20 20 20 20 65 6e 64 0a   + "/").    end.
55f0: 20 20 65 6c 73 65 0a 20 20 20 20 61 62 6f 72 74    else.    abort
5600: 20 22 75 6e 72 65 63 6f 67 6e 69 73 65 64 20 6d   "unrecognised m
5610: 6f 64 65 22 2c 20 32 0a 20 20 65 6e 64 0a 20 20  ode", 2.  end.  
5620: 23 20 45 78 65 63 20 64 69 72 65 63 74 6c 79 3a  # Exec directly:
5630: 20 74 68 65 20 72 65 63 65 69 70 74 20 43 47 49   the receipt CGI
5640: 20 68 61 73 20 63 6c 6f 73 65 64 20 74 68 65 20   has closed the 
5650: 72 75 6e 74 69 6d 65 27 73 20 64 65 73 63 72 69  runtime's descri
5660: 70 74 6f 72 73 2e 0a 20 20 61 72 67 76 20 3d 20  ptors..  argv = 
5670: 28 5b 22 2f 75 73 72 2f 62 69 6e 2f 62 77 72 61  (["/usr/bin/bwra
5680: 70 22 5d 20 2b 20 61 72 67 73 29 2e 6d 61 70 28  p"] + args).map(
5690: 26 2e 74 6f 5f 75 6e 73 61 66 65 29 20 2b 20 5b  &.to_unsafe) + [
56a0: 50 6f 69 6e 74 65 72 28 55 49 6e 74 38 29 2e 6e  Pointer(UInt8).n
56b0: 75 6c 6c 5d 0a 20 20 4c 69 62 43 2e 65 78 65 63  ull].  LibC.exec
56c0: 76 28 22 2f 75 73 72 2f 62 69 6e 2f 62 77 72 61  v("/usr/bin/bwra
56d0: 70 22 2c 20 61 72 67 76 2e 74 6f 5f 75 6e 73 61  p", argv.to_unsa
56e0: 66 65 29 0a 20 20 72 61 69 73 65 20 49 4f 3a 3a  fe).  raise IO::
56f0: 45 72 72 6f 72 2e 66 72 6f 6d 5f 65 72 72 6e 6f  Error.from_errno
5700: 28 22 65 78 65 63 76 22 29 0a 72 65 73 63 75 65  ("execv").rescue
5710: 20 65 78 20 3a 20 41 72 67 75 6d 65 6e 74 45 72   ex : ArgumentEr
5720: 72 6f 72 20 7c 20 49 4f 3a 3a 45 72 72 6f 72 0a  ror | IO::Error.
5730: 20 20 53 54 44 45 52 52 2e 70 75 74 73 20 65 78    STDERR.puts ex
5740: 2e 6d 65 73 73 61 67 65 0a 20 20 65 78 69 74 20  .message.  exit 
5750: 32 0a 65 6e 64 0a 00 00 00 00 00 00 00 00 00 00  2.end...........
5760: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5770: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5790: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
57a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
57b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
57c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
57d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
57e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
57f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5800: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69  scripts-for-revi
5810: 65 77 2d 33 2f 73 79 6e 63 2d 66 6f 73 73 69 6c  ew-3/sync-fossil
5820: 2e 63 72 00 00 00 00 00 00 00 00 00 00 00 00 00  .cr.............
5830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5860: 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30  ....0000644.0000
5870: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30  000.0000000.0000
5880: 30 30 30 34 34 34 35 00 31 35 32 37 30 36 36 34  0004445.15270664
5890: 36 31 33 00 30 31 36 33 30 36 00 20 30 00 00 00  613.016306. 0...
58a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
58b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
58c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
58d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
58e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
58f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5900: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00  .ustar  .root...
5910: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5920: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00  .........root...
5930: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5940: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
59a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
59b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
59c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
59d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
59e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
59f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
5a00: 23 20 41 67 65 6e 74 73 3a 20 64 6f 20 6e 6f 74  # Agents: do not
5a10: 20 63 68 61 6e 67 65 20 74 68 69 73 20 66 69 6c   change this fil
5a20: 65 20 6f 6e 20 61 6e 79 20 62 72 61 6e 63 68 20  e on any branch 
5a30: 77 69 74 68 6f 75 74 20 74 68 65 20 6d 61 69 6e  without the main
5a40: 74 61 69 6e 65 72 27 73 20 65 78 70 6c 69 63 69  tainer's explici
5a50: 74 20 61 70 70 72 6f 76 61 6c 20 6f 6e 20 74 68  t approval on th
5a60: 65 20 72 65 63 6f 72 64 2e 0a 23 20 53 79 6e 63  e record..# Sync
5a70: 20 6f 72 20 63 6c 6f 6e 65 20 72 65 70 6f 73 69   or clone reposi
5a80: 74 6f 72 69 65 73 3b 20 73 65 74 74 69 6e 67 73  tories; settings
5a90: 20 61 72 65 20 69 6e 20 74 68 65 20 73 65 72 76   are in the serv
5aa0: 65 72 73 2e 6a 73 6f 6e 20 62 65 73 69 64 65 20  ers.json beside 
5ab0: 74 68 69 73 20 70 72 6f 67 72 61 6d 2e 0a 23 20  this program..# 
5ac0: 54 68 65 20 6d 61 69 6e 74 61 69 6e 65 72 20 72  The maintainer r
5ad0: 75 6e 73 20 62 69 6e 2f 73 79 6e 63 2d 66 6f 73  uns bin/sync-fos
5ae0: 73 69 6c 20 61 66 74 65 72 20 73 68 61 72 64 73  sil after shards
5af0: 20 62 75 69 6c 64 20 73 79 6e 63 2d 66 6f 73 73   build sync-foss
5b00: 69 6c 2e 0a 72 65 71 75 69 72 65 20 22 6a 73 6f  il..require "jso
5b10: 6e 22 0a 53 45 52 56 45 52 20 3d 20 22 68 74 74  n".SERVER = "htt
5b20: 70 73 3a 2f 2f 6f 6c 6c 61 6d 61 2e 6f 70 65 6e  ps://ollama.open
5b30: 62 65 61 67 6c 65 2e 6f 72 67 22 20 23 20 77 68  beagle.org" # wh
5b40: 65 72 65 20 63 6c 6f 6e 65 73 20 63 6f 6d 65 20  ere clones come 
5b50: 66 72 6f 6d 0a 0a 73 74 72 75 63 74 20 53 65 72  from..struct Ser
5b60: 76 65 72 0a 20 20 69 6e 63 6c 75 64 65 20 4a 53  ver.  include JS
5b70: 4f 4e 3a 3a 53 65 72 69 61 6c 69 7a 61 62 6c 65  ON::Serializable
5b80: 0a 20 20 67 65 74 74 65 72 20 73 79 6e 63 20 3a  .  getter sync :
5b90: 20 41 72 72 61 79 28 53 74 72 69 6e 67 29 3f 0a   Array(String)?.
5ba0: 65 6e 64 0a 0a 66 69 6c 65 20 3d 20 46 69 6c 65  end..file = File
5bb0: 2e 6a 6f 69 6e 28 46 69 6c 65 2e 64 69 72 6e 61  .join(File.dirna
5bc0: 6d 65 28 50 72 6f 63 65 73 73 2e 65 78 65 63 75  me(Process.execu
5bd0: 74 61 62 6c 65 5f 70 61 74 68 2e 6e 6f 74 5f 6e  table_path.not_n
5be0: 69 6c 21 29 2c 20 22 73 65 72 76 65 72 73 2e 6a  il!), "servers.j
5bf0: 73 6f 6e 22 29 0a 62 65 67 69 6e 0a 20 20 73 65  son").begin.  se
5c00: 72 76 65 72 73 20 3d 20 48 61 73 68 28 53 74 72  rvers = Hash(Str
5c10: 69 6e 67 2c 20 4a 53 4f 4e 3a 3a 41 6e 79 29 2e  ing, JSON::Any).
5c20: 66 72 6f 6d 5f 6a 73 6f 6e 28 46 69 6c 65 2e 72  from_json(File.r
5c30: 65 61 64 28 66 69 6c 65 29 29 0a 20 20 6c 6f 63  ead(file)).  loc
5c40: 61 6c 20 3d 20 73 65 72 76 65 72 73 5b 53 79 73  al = servers[Sys
5c50: 74 65 6d 2e 68 6f 73 74 6e 61 6d 65 5d 3f 2e 74  tem.hostname]?.t
5c60: 72 79 20 7b 20 7c 65 6e 74 72 79 7c 20 53 65 72  ry { |entry| Ser
5c70: 76 65 72 2e 66 72 6f 6d 5f 6a 73 6f 6e 28 65 6e  ver.from_json(en
5c80: 74 72 79 2e 74 6f 5f 6a 73 6f 6e 29 20 7d 0a 20  try.to_json) }. 
5c90: 20 64 65 66 61 75 6c 74 20 3d 20 73 65 72 76 65   default = serve
5ca0: 72 73 5b 22 2a 22 5d 3f 2e 74 72 79 20 7b 20 7c  rs["*"]?.try { |
5cb0: 65 6e 74 72 79 7c 20 53 65 72 76 65 72 2e 66 72  entry| Server.fr
5cc0: 6f 6d 5f 6a 73 6f 6e 28 65 6e 74 72 79 2e 74 6f  om_json(entry.to
5cd0: 5f 6a 73 6f 6e 29 20 7d 0a 20 20 72 65 70 6f 73  _json) }.  repos
5ce0: 69 74 6f 72 69 65 73 20 3d 20 6c 6f 63 61 6c 2e  itories = local.
5cf0: 74 72 79 28 26 2e 73 79 6e 63 29 20 7c 7c 20 64  try(&.sync) || d
5d00: 65 66 61 75 6c 74 2e 74 72 79 28 26 2e 73 79 6e  efault.try(&.syn
5d10: 63 29 20 7c 7c 20 61 62 6f 72 74 20 22 23 7b 66  c) || abort "#{f
5d20: 69 6c 65 7d 3a 20 6d 69 73 73 69 6e 67 20 73 79  ile}: missing sy
5d30: 6e 63 22 2c 20 32 0a 72 65 73 63 75 65 20 65 78  nc", 2.rescue ex
5d40: 20 3a 20 49 4f 3a 3a 45 72 72 6f 72 20 7c 20 4a   : IO::Error | J
5d50: 53 4f 4e 3a 3a 50 61 72 73 65 45 78 63 65 70 74  SON::ParseExcept
5d60: 69 6f 6e 0a 20 20 61 62 6f 72 74 20 22 23 7b 66  ion.  abort "#{f
5d70: 69 6c 65 7d 3a 20 63 61 6e 6e 6f 74 20 72 65 61  ile}: cannot rea
5d80: 64 20 73 79 6e 63 3a 20 23 7b 65 78 2e 6d 65 73  d sync: #{ex.mes
5d90: 73 61 67 65 2e 74 6f 5f 73 2e 6c 69 6e 65 73 2e  sage.to_s.lines.
5da0: 66 69 72 73 74 7d 22 2c 20 32 0a 65 6e 64 0a 0a  first}", 2.end..
5db0: 23 20 46 69 6e 64 20 74 68 65 20 64 69 72 65 63  # Find the direc
5dc0: 74 6f 72 79 20 61 62 6f 76 65 20 74 68 69 73 20  tory above this 
5dd0: 63 68 65 63 6b 6f 75 74 2e 0a 68 6f 73 74 20 3d  checkout..host =
5de0: 20 53 79 73 74 65 6d 2e 68 6f 73 74 6e 61 6d 65   System.hostname
5df0: 0a 6d 65 20 3d 20 50 72 6f 63 65 73 73 2e 65 78  .me = Process.ex
5e00: 65 63 75 74 61 62 6c 65 5f 70 61 74 68 20 7c 7c  ecutable_path ||
5e10: 20 61 62 6f 72 74 20 22 63 61 6e 6e 6f 74 20 74   abort "cannot t
5e20: 65 6c 6c 20 77 68 65 72 65 20 74 68 69 73 20 70  ell where this p
5e30: 72 6f 67 72 61 6d 20 69 73 22 0a 62 61 73 65 64  rogram is".based
5e40: 69 72 20 3d 20 46 69 6c 65 2e 72 65 61 6c 70 61  ir = File.realpa
5e50: 74 68 28 46 69 6c 65 2e 6a 6f 69 6e 28 46 69 6c  th(File.join(Fil
5e60: 65 2e 64 69 72 6e 61 6d 65 28 6d 65 29 2c 20 22  e.dirname(me), "
5e70: 2e 2e 22 2c 20 22 2e 2e 22 29 29 0a 70 75 74 73  ..", "..")).puts
5e80: 20 22 52 75 6e 6e 69 6e 67 20 6f 6e 20 23 7b 68   "Running on #{h
5e90: 6f 73 74 7d 22 0a 70 75 74 73 20 22 52 75 6e 6e  ost}".puts "Runn
5ea0: 69 6e 67 20 66 72 6f 6d 20 23 7b 62 61 73 65 64  ing from #{based
5eb0: 69 72 7d 22 0a 0a 23 20 52 65 61 64 20 46 6f 73  ir}"..# Read Fos
5ec0: 73 69 6c 27 73 20 73 74 61 74 75 73 20 61 6e 64  sil's status and
5ed0: 20 77 6f 72 64 73 20 74 6f 20 64 65 63 69 64 65   words to decide
5ee0: 20 77 68 65 74 68 65 72 20 69 74 20 66 61 69 6c   whether it fail
5ef0: 65 64 2e 0a 64 65 66 20 66 61 69 6c 75 72 65 28  ed..def failure(
5f00: 73 74 61 74 75 73 20 3a 20 50 72 6f 63 65 73 73  status : Process
5f10: 3a 3a 53 74 61 74 75 73 2c 20 6f 75 74 70 75 74  ::Status, output
5f20: 20 3a 20 53 74 72 69 6e 67 29 20 3a 20 53 74 72   : String) : Str
5f30: 69 6e 67 3f 0a 20 20 72 65 74 75 72 6e 20 22 66  ing?.  return "f
5f40: 6f 73 73 69 6c 20 65 6e 64 65 64 20 61 62 6e 6f  ossil ended abno
5f50: 72 6d 61 6c 6c 79 20 28 23 7b 73 74 61 74 75 73  rmally (#{status
5f60: 2e 65 78 69 74 5f 72 65 61 73 6f 6e 7d 29 22 20  .exit_reason})" 
5f70: 75 6e 6c 65 73 73 20 73 74 61 74 75 73 2e 6e 6f  unless status.no
5f80: 72 6d 61 6c 5f 65 78 69 74 3f 0a 20 20 72 65 74  rmal_exit?.  ret
5f90: 75 72 6e 20 22 66 6f 73 73 69 6c 20 65 78 69 74  urn "fossil exit
5fa0: 65 64 20 23 7b 73 74 61 74 75 73 2e 65 78 69 74  ed #{status.exit
5fb0: 5f 63 6f 64 65 7d 22 20 75 6e 6c 65 73 73 20 73  _code}" unless s
5fc0: 74 61 74 75 73 2e 73 75 63 63 65 73 73 3f 0a 20  tatus.success?. 
5fd0: 20 69 66 20 6c 69 6e 65 20 3d 20 6f 75 74 70 75   if line = outpu
5fe0: 74 2e 65 61 63 68 5f 6c 69 6e 65 2e 6d 61 70 28  t.each_line.map(
5ff0: 26 2e 73 70 6c 69 74 28 27 5c 72 27 29 2e 6c 61  &.split('\r').la
6000: 73 74 2e 73 74 72 69 70 29 2e 66 69 6e 64 28 26  st.strip).find(&
6010: 2e 73 74 61 72 74 73 5f 77 69 74 68 3f 28 22 45  .starts_with?("E
6020: 72 72 6f 72 3a 22 29 29 0a 20 20 20 20 72 65 74  rror:")).    ret
6030: 75 72 6e 20 6c 69 6e 65 0a 20 20 65 6e 64 0a 20  urn line.  end. 
6040: 20 22 69 74 20 6e 65 76 65 72 20 72 65 61 63 68   "it never reach
6050: 65 64 20 74 68 65 20 73 65 72 76 65 72 20 28 52  ed the server (R
6060: 6f 75 6e 64 2d 74 72 69 70 73 3a 20 30 29 22 20  ound-trips: 0)" 
6070: 69 66 20 6f 75 74 70 75 74 2e 69 6e 63 6c 75 64  if output.includ
6080: 65 73 3f 28 22 52 6f 75 6e 64 2d 74 72 69 70 73  es?("Round-trips
6090: 3a 20 30 20 22 29 0a 65 6e 64 0a 0a 23 20 53 79  : 0 ").end..# Sy
60a0: 6e 63 20 6f 72 20 63 6c 6f 6e 65 20 65 61 63 68  nc or clone each
60b0: 20 72 65 70 6f 73 69 74 6f 72 79 3b 20 73 74 6f   repository; sto
60c0: 70 20 61 74 20 74 68 65 20 66 69 72 73 74 20 66  p at the first f
60d0: 61 69 6c 75 72 65 2e 0a 72 65 70 6f 73 69 74 6f  ailure..reposito
60e0: 72 69 65 73 2e 65 61 63 68 20 64 6f 20 7c 64 7c  ries.each do |d|
60f0: 0a 20 20 70 75 74 73 20 22 5c 6e 5c 6e 5c 74 2a  .  puts "\n\n\t*
6100: 2a 2a 2a 5b 20 23 7b 62 61 73 65 64 69 72 7d 2f  ***[ #{basedir}/
6110: 23 7b 64 7d 20 5d 2a 2a 2a 2a 5c 6e 5c 6e 22 0a  #{d} ]****\n\n".
6120: 20 20 64 69 72 20 3d 20 46 69 6c 65 2e 6a 6f 69    dir = File.joi
6130: 6e 28 62 61 73 65 64 69 72 2c 20 64 29 0a 20 20  n(basedir, d).  
6140: 23 20 53 68 6f 77 20 61 6e 64 20 6b 65 65 70 20  # Show and keep 
6150: 46 6f 73 73 69 6c 27 73 20 6f 75 74 70 75 74 2e  Fossil's output.
6160: 0a 20 20 73 61 69 64 20 3d 20 49 4f 3a 3a 4d 65  .  said = IO::Me
6170: 6d 6f 72 79 2e 6e 65 77 0a 20 20 73 68 6f 77 6e  mory.new.  shown
6180: 20 3d 20 49 4f 3a 3a 4d 75 6c 74 69 57 72 69 74   = IO::MultiWrit
6190: 65 72 2e 6e 65 77 28 53 54 44 4f 55 54 2c 20 73  er.new(STDOUT, s
61a0: 61 69 64 29 0a 20 20 73 74 61 74 75 73 20 3d 20  aid).  status = 
61b0: 69 66 20 44 69 72 2e 65 78 69 73 74 73 3f 28 64  if Dir.exists?(d
61c0: 69 72 29 0a 20 20 20 20 20 20 20 20 20 20 20 20  ir).            
61d0: 20 50 72 6f 63 65 73 73 2e 72 75 6e 28 22 66 6f   Process.run("fo
61e0: 73 73 69 6c 22 2c 20 5b 22 73 79 6e 63 22 2c 20  ssil", ["sync", 
61f0: 22 2d 2d 61 6c 6c 22 5d 2c 20 63 68 64 69 72 3a  "--all"], chdir:
6200: 20 64 69 72 2c 20 6f 75 74 70 75 74 3a 20 73 68   dir, output: sh
6210: 6f 77 6e 2c 20 65 72 72 6f 72 3a 20 73 68 6f 77  own, error: show
6220: 6e 29 0a 20 20 20 20 20 20 20 20 20 20 20 65 6c  n).           el
6230: 73 65 0a 20 20 20 20 20 20 20 20 20 20 20 20 20  se.             
6240: 50 72 6f 63 65 73 73 2e 72 75 6e 28 22 66 6f 73  Process.run("fos
6250: 73 69 6c 22 2c 20 5b 22 63 6c 6f 6e 65 22 2c 20  sil", ["clone", 
6260: 22 23 7b 53 45 52 56 45 52 7d 2f 23 7b 64 7d 22  "#{SERVER}/#{d}"
6270: 5d 2c 20 63 68 64 69 72 3a 20 62 61 73 65 64 69  ], chdir: basedi
6280: 72 2c 20 6f 75 74 70 75 74 3a 20 73 68 6f 77 6e  r, output: shown
6290: 2c 20 65 72 72 6f 72 3a 20 73 68 6f 77 6e 29 0a  , error: shown).
62a0: 20 20 20 20 20 20 20 20 20 20 20 65 6e 64 0a 20             end. 
62b0: 20 69 66 20 72 65 61 73 6f 6e 20 3d 20 66 61 69   if reason = fai
62c0: 6c 75 72 65 28 73 74 61 74 75 73 2c 20 73 61 69  lure(status, sai
62d0: 64 2e 74 6f 5f 73 29 0a 20 20 20 20 53 54 44 45  d.to_s).    STDE
62e0: 52 52 2e 70 75 74 73 20 22 5c 6e 73 79 6e 63 2d  RR.puts "\nsync-
62f0: 66 6f 73 73 69 6c 3a 20 23 7b 64 7d 20 66 61 69  fossil: #{d} fai
6300: 6c 65 64 3a 20 23 7b 72 65 61 73 6f 6e 7d 22 0a  led: #{reason}".
6310: 20 20 20 20 65 78 69 74 20 31 0a 20 20 65 6e 64      exit 1.  end
6320: 0a 65 6e 64 0a 00 00 00 00 00 00 00 00 00 00 00  .end............
6330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6350: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6360: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6390: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
63a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
63b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
63c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
63d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
63e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
63f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6400: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69  scripts-for-revi
6410: 65 77 2d 33 2f 73 65 72 76 65 72 73 2e 6a 73 6f  ew-3/servers.jso
6420: 6e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  n...............
6430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6460: 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30  ....0000644.0000
6470: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30  000.0000000.0000
6480: 30 30 30 36 32 36 36 00 31 35 32 37 30 36 36 34  0006266.15270664
6490: 36 31 33 00 30 31 36 30 37 36 00 20 30 00 00 00  613.016076. 0...
64a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
64b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
64c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
64d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
64e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
64f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6500: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00  .ustar  .root...
6510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6520: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00  .........root...
6530: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6540: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6550: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6560: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6570: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6590: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
65a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
65b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
65c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
65d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
65e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
65f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
6600: 7b 0a 20 20 22 5f 61 62 6f 75 74 22 3a 20 22 48  {.  "_about": "H
6610: 6f 73 74 20 64 65 70 6c 6f 79 6d 65 6e 74 20 73  ost deployment s
6620: 65 74 74 69 6e 67 73 3b 20 63 6f 70 79 20 73 65  ettings; copy se
6630: 72 76 65 72 73 2e 6a 73 6f 6e 20 62 65 73 69 64  rvers.json besid
6640: 65 20 65 61 63 68 20 64 65 70 6c 6f 79 65 64 20  e each deployed 
6650: 70 72 6f 67 72 61 6d 2e 22 2c 0a 20 20 22 69 70  program.",.  "ip
6660: 2d 31 37 32 2d 33 31 2d 31 37 2d 31 37 37 2e 75  -172-31-17-177.u
6670: 73 2d 77 65 73 74 2d 32 2e 63 6f 6d 70 75 74 65  s-west-2.compute
6680: 2e 69 6e 74 65 72 6e 61 6c 22 3a 20 7b 0a 20 20  .internal": {.  
6690: 20 20 22 66 6f 73 73 69 6c 5f 73 65 72 76 65 72    "fossil_server
66a0: 22 3a 20 7b 0a 20 20 20 20 20 20 22 66 6f 73 73  ": {.      "foss
66b0: 69 6c 22 3a 20 22 2f 75 73 72 2f 62 69 6e 2f 66  il": "/usr/bin/f
66c0: 6f 73 73 69 6c 22 2c 0a 20 20 20 20 20 20 22 61  ossil",.      "a
66d0: 72 67 73 22 3a 20 5b 0a 20 20 20 20 20 20 20 20  rgs": [.        
66e0: 22 73 65 72 76 65 72 22 2c 0a 20 20 20 20 20 20  "server",.      
66f0: 20 20 22 2d 2d 62 61 73 65 75 72 6c 22 2c 20 22    "--baseurl", "
6700: 68 74 74 70 73 3a 2f 2f 6f 6c 6c 61 6d 61 2e 6f  https://ollama.o
6710: 70 65 6e 62 65 61 67 6c 65 2e 6f 72 67 22 2c 0a  penbeagle.org",.
6720: 20 20 20 20 20 20 20 20 22 2d 2d 70 6f 72 74 22          "--port"
6730: 2c 20 22 34 34 33 22 2c 0a 20 20 20 20 20 20 20  , "443",.       
6740: 20 22 2d 2d 6a 73 6d 6f 64 65 22 2c 20 22 62 75   "--jsmode", "bu
6750: 6e 64 6c 65 64 22 2c 0a 20 20 20 20 20 20 20 20  ndled",.        
6760: 22 2d 2d 63 65 72 74 22 2c 20 22 2f 65 74 63 2f  "--cert", "/etc/
6770: 6c 65 74 73 65 6e 63 72 79 70 74 2f 6c 69 76 65  letsencrypt/live
6780: 2f 6f 6c 6c 61 6d 61 2e 6f 70 65 6e 62 65 61 67  /ollama.openbeag
6790: 6c 65 2e 6f 72 67 2f 66 75 6c 6c 63 68 61 69 6e  le.org/fullchain
67a0: 2e 70 65 6d 22 2c 0a 20 20 20 20 20 20 20 20 22  .pem",.        "
67b0: 2d 2d 70 6b 65 79 22 2c 20 22 2f 65 74 63 2f 6c  --pkey", "/etc/l
67c0: 65 74 73 65 6e 63 72 79 70 74 2f 6c 69 76 65 2f  etsencrypt/live/
67d0: 6f 6c 6c 61 6d 61 2e 6f 70 65 6e 62 65 61 67 6c  ollama.openbeagl
67e0: 65 2e 6f 72 67 2f 70 72 69 76 6b 65 79 2e 70 65  e.org/privkey.pe
67f0: 6d 22 2c 0a 20 20 20 20 20 20 20 20 22 2d 2d 61  m",.        "--a
6800: 63 6d 65 22 2c 0a 20 20 20 20 20 20 20 20 22 2d  cme",.        "-
6810: 2d 6e 6f 6a 61 69 6c 22 2c 0a 20 20 20 20 20 20  -nojail",.      
6820: 20 20 22 2d 2d 72 65 70 6f 6c 69 73 74 22 2c 20    "--repolist", 
6830: 22 2f 76 61 72 2f 6c 69 62 2f 66 6f 73 73 69 6c  "/var/lib/fossil
6840: 22 2c 0a 20 20 20 20 20 20 20 20 22 2d 2d 65 78  ",.        "--ex
6850: 74 72 6f 6f 74 22 2c 20 22 2f 76 61 72 2f 77 77  troot", "/var/ww
6860: 77 2f 63 67 69 2d 62 69 6e 22 2c 0a 20 20 20 20  w/cgi-bin",.    
6870: 20 20 20 20 22 2d 2d 6e 6f 74 66 6f 75 6e 64 22      "--notfound"
6880: 2c 20 22 68 74 74 70 73 3a 2f 2f 6f 6c 6c 61 6d  , "https://ollam
6890: 61 2e 6f 70 65 6e 62 65 61 67 6c 65 2e 6f 72 67  a.openbeagle.org
68a0: 2f 69 6e 64 65 78 2e 68 74 6d 6c 22 2c 0a 20 20  /index.html",.  
68b0: 20 20 20 20 20 20 22 2d 2d 66 69 6c 65 73 22 2c        "--files",
68c0: 20 22 2a 2e 69 63 6f 2c 2a 2e 68 74 6d 6c 22 2c   "*.ico,*.html",
68d0: 0a 20 20 20 20 20 20 20 20 22 2d 2d 65 72 72 6f  .        "--erro
68e0: 72 6c 6f 67 22 2c 20 22 2f 76 61 72 2f 6c 6f 67  rlog", "/var/log
68f0: 2f 66 6f 73 73 69 6c 2e 65 72 72 2e 6c 6f 67 22  /fossil.err.log"
6900: 0a 20 20 20 20 20 20 5d 0a 20 20 20 20 7d 2c 0a  .      ].    },.
6910: 20 20 20 20 22 6f 63 78 22 3a 20 7b 0a 20 20 20      "ocx": {.   
6920: 20 20 20 22 72 65 70 6f 73 69 74 6f 72 69 65 73     "repositories
6930: 22 3a 20 22 2f 76 61 72 2f 6c 69 62 2f 66 6f 73  ": "/var/lib/fos
6940: 73 69 6c 22 2c 0a 20 20 20 20 20 20 22 73 6f 63  sil",.      "soc
6950: 6b 65 74 5f 64 69 72 22 3a 20 22 2f 72 75 6e 2f  ket_dir": "/run/
6960: 6f 63 78 22 2c 0a 20 20 20 20 20 20 22 72 65 6c  ocx",.      "rel
6970: 65 61 73 65 73 22 3a 20 22 2f 76 61 72 2f 6c 69  eases": "/var/li
6980: 62 2f 6f 63 78 2f 72 65 6c 65 61 73 65 32 22 2c  b/ocx/release2",
6990: 0a 20 20 20 20 20 20 22 64 61 65 6d 6f 6e 5f 72  .      "daemon_r
69a0: 6f 6f 74 22 3a 20 22 2f 76 61 72 2f 6c 69 62 2f  oot": "/var/lib/
69b0: 6f 63 78 2f 61 67 65 6e 74 73 2d 64 61 65 6d 6f  ocx/agents-daemo
69c0: 6e 22 2c 0a 20 20 20 20 20 20 22 68 6f 6d 65 73  n",.      "homes
69d0: 22 3a 20 7b 0a 20 20 20 20 20 20 20 20 22 2f 68  ": {.        "/h
69e0: 6f 6d 65 2f 6f 63 78 2f 2e 63 6f 64 65 78 22 3a  ome/ocx/.codex":
69f0: 20 22 2f 72 6f 6f 74 2f 2e 63 6f 64 65 78 22 2c   "/root/.codex",
6a00: 0a 20 20 20 20 20 20 20 20 22 2f 68 6f 6d 65 2f  .        "/home/
6a10: 6f 63 78 2f 2e 63 6c 61 75 64 65 22 3a 20 22 2f  ocx/.claude": "/
6a20: 72 6f 6f 74 2f 2e 63 6c 61 75 64 65 22 2c 0a 20  root/.claude",. 
6a30: 20 20 20 20 20 20 20 22 2f 68 6f 6d 65 2f 6f 63         "/home/oc
6a40: 78 2f 2e 63 6c 61 75 64 65 2e 6a 73 6f 6e 22 3a  x/.claude.json":
6a50: 20 22 2f 72 6f 6f 74 2f 2e 63 6c 61 75 64 65 2e   "/root/.claude.
6a60: 6a 73 6f 6e 22 0a 20 20 20 20 20 20 7d 2c 0a 20  json".      },. 
6a70: 20 20 20 20 20 22 74 6f 6f 6c 73 22 3a 20 7b 0a       "tools": {.
6a80: 20 20 20 20 20 20 20 20 22 2f 68 6f 6d 65 2f 6f          "/home/o
6a90: 63 78 2f 2e 6c 6f 63 61 6c 22 3a 20 22 2f 72 6f  cx/.local": "/ro
6aa0: 6f 74 2f 2e 6c 6f 63 61 6c 22 0a 20 20 20 20 20  ot/.local".     
6ab0: 20 7d 2c 0a 20 20 20 20 20 20 22 6c 61 75 6e 63   },.      "launc
6ac0: 68 65 72 22 3a 20 22 2f 75 73 72 2f 6c 6f 63 61  her": "/usr/loca
6ad0: 6c 2f 6c 69 62 65 78 65 63 2f 6f 63 78 2f 73 74  l/libexec/ocx/st
6ae0: 61 72 74 2d 6f 63 78 22 0a 20 20 20 20 7d 0a 20  art-ocx".    }. 
6af0: 20 7d 2c 0a 20 20 22 62 65 61 67 6c 65 79 2d 73   },.  "beagley-s
6b00: 73 64 22 3a 20 7b 0a 20 20 20 20 22 66 6f 73 73  sd": {.    "foss
6b10: 69 6c 5f 73 65 72 76 65 72 22 3a 20 7b 0a 20 20  il_server": {.  
6b20: 20 20 20 20 22 61 72 67 73 22 3a 20 5b 0a 20 20      "args": [.  
6b30: 20 20 20 20 20 20 22 73 65 72 76 65 72 22 2c 0a        "server",.
6b40: 20 20 20 20 20 20 20 20 22 2d 2d 70 6f 72 74 22          "--port"
6b50: 2c 20 22 34 34 33 22 2c 0a 20 20 20 20 20 20 20  , "443",.       
6b60: 20 22 2d 2d 63 65 72 74 22 2c 20 22 24 48 4f 4d   "--cert", "$HOM
6b70: 45 2f 2e 63 6f 6e 66 69 67 2f 66 6f 73 73 69 6c  E/.config/fossil
6b80: 2f 74 6c 73 2f 63 65 72 74 69 66 69 63 61 74 65  /tls/certificate
6b90: 2e 70 65 6d 22 2c 0a 20 20 20 20 20 20 20 20 22  .pem",.        "
6ba0: 2d 2d 70 6b 65 79 22 2c 20 22 24 48 4f 4d 45 2f  --pkey", "$HOME/
6bb0: 2e 63 6f 6e 66 69 67 2f 66 6f 73 73 69 6c 2f 74  .config/fossil/t
6bc0: 6c 73 2f 70 72 69 76 61 74 65 2d 6b 65 79 2e 70  ls/private-key.p
6bd0: 65 6d 22 2c 0a 20 20 20 20 20 20 20 20 22 2d 2d  em",.        "--
6be0: 6a 73 6d 6f 64 65 22 2c 20 22 62 75 6e 64 6c 65  jsmode", "bundle
6bf0: 64 22 2c 0a 20 20 20 20 20 20 20 20 22 2d 2d 72  d",.        "--r
6c00: 65 70 6f 6c 69 73 74 22 2c 20 22 24 42 41 53 45  epolist", "$BASE
6c10: 44 49 52 22 2c 0a 20 20 20 20 20 20 20 20 22 2d  DIR",.        "-
6c20: 2d 65 78 74 72 6f 6f 74 22 2c 20 22 24 48 4f 4d  -extroot", "$HOM
6c30: 45 2f 2e 6c 6f 63 61 6c 2f 63 67 69 2d 62 69 6e  E/.local/cgi-bin
6c40: 22 2c 0a 20 20 20 20 20 20 20 20 22 2d 2d 6e 6f  ",.        "--no
6c50: 6a 61 69 6c 22 2c 0a 20 20 20 20 20 20 20 20 22  jail",.        "
6c60: 2d 2d 73 6b 69 6e 22 2c 20 22 64 61 72 6b 6d 6f  --skin", "darkmo
6c70: 64 65 22 2c 0a 20 20 20 20 20 20 20 20 22 2d 2d  de",.        "--
6c80: 65 72 72 6f 72 6c 6f 67 22 2c 20 22 24 42 41 53  errorlog", "$BAS
6c90: 45 44 49 52 2f 66 6f 73 73 69 6c 2e 65 72 72 2e  EDIR/fossil.err.
6ca0: 6c 6f 67 22 0a 20 20 20 20 20 20 5d 2c 0a 20 20  log".      ],.  
6cb0: 20 20 20 20 22 73 68 6f 77 5f 63 61 70 61 62 69      "show_capabi
6cc0: 6c 69 74 69 65 73 22 3a 20 74 72 75 65 0a 20 20  lities": true.  
6cd0: 20 20 7d 0a 20 20 7d 2c 0a 20 20 22 62 65 61 67    }.  },.  "beag
6ce0: 6c 65 62 61 64 67 65 22 3a 20 7b 0a 20 20 20 20  lebadge": {.    
6cf0: 22 66 6f 73 73 69 6c 5f 73 65 72 76 65 72 22 3a  "fossil_server":
6d00: 20 7b 0a 20 20 20 20 20 20 22 61 72 67 73 22 3a   {.      "args":
6d10: 20 5b 0a 20 20 20 20 20 20 20 20 22 73 65 72 76   [.        "serv
6d20: 65 72 22 2c 0a 20 20 20 20 20 20 20 20 22 2d 2d  er",.        "--
6d30: 70 6f 72 74 22 2c 20 22 38 30 22 2c 0a 20 20 20  port", "80",.   
6d40: 20 20 20 20 20 22 2d 2d 6a 73 6d 6f 64 65 22 2c       "--jsmode",
6d50: 20 22 62 75 6e 64 6c 65 64 22 2c 0a 20 20 20 20   "bundled",.    
6d60: 20 20 20 20 22 2d 2d 72 65 70 6f 6c 69 73 74 22      "--repolist"
6d70: 2c 20 22 24 42 41 53 45 44 49 52 22 2c 0a 20 20  , "$BASEDIR",.  
6d80: 20 20 20 20 20 20 22 2d 2d 6e 6f 6a 61 69 6c 22        "--nojail"
6d90: 2c 0a 20 20 20 20 20 20 20 20 22 2d 2d 65 72 72  ,.        "--err
6da0: 6f 72 6c 6f 67 22 2c 20 22 24 42 41 53 45 44 49  orlog", "$BASEDI
6db0: 52 2f 66 6f 73 73 69 6c 2e 65 72 72 2e 6c 6f 67  R/fossil.err.log
6dc0: 22 0a 20 20 20 20 20 20 5d 0a 20 20 20 20 7d 0a  ".      ].    }.
6dd0: 20 20 7d 2c 0a 20 20 22 73 6c 6f 74 63 61 72 22    },.  "slotcar"
6de0: 3a 20 7b 0a 20 20 20 20 22 66 6f 73 73 69 6c 5f  : {.    "fossil_
6df0: 73 65 72 76 65 72 22 3a 20 7b 0a 20 20 20 20 20  server": {.     
6e00: 20 22 61 72 67 73 22 3a 20 5b 0a 20 20 20 20 20   "args": [.     
6e10: 20 20 20 22 73 65 72 76 65 72 22 2c 0a 20 20 20     "server",.   
6e20: 20 20 20 20 20 22 2d 2d 72 65 70 6f 6c 69 73 74       "--repolist
6e30: 22 2c 20 22 24 42 41 53 45 44 49 52 22 2c 0a 20  ", "$BASEDIR",. 
6e40: 20 20 20 20 20 20 20 22 2d 2d 6e 6f 6a 61 69 6c         "--nojail
6e50: 22 2c 0a 20 20 20 20 20 20 20 20 22 2d 2d 73 6b  ",.        "--sk
6e60: 69 6e 22 2c 20 22 64 61 72 6b 6d 6f 64 65 22 2c  in", "darkmode",
6e70: 0a 20 20 20 20 20 20 20 20 22 2d 2d 65 78 74 72  .        "--extr
6e80: 6f 6f 74 22 2c 20 22 24 42 41 53 45 44 49 52 2f  oot", "$BASEDIR/
6e90: 6f 6c 6c 61 6d 61 2f 62 69 6e 22 2c 0a 20 20 20  ollama/bin",.   
6ea0: 20 20 20 20 20 22 2d 2d 65 72 72 6f 72 6c 6f 67       "--errorlog
6eb0: 22 2c 20 22 24 42 41 53 45 44 49 52 2f 66 6f 73  ", "$BASEDIR/fos
6ec0: 73 69 6c 2e 65 72 72 2e 6c 6f 67 22 0a 20 20 20  sil.err.log".   
6ed0: 20 20 20 5d 0a 20 20 20 20 7d 0a 20 20 7d 2c 0a     ].    }.  },.
6ee0: 20 20 22 2a 22 3a 20 7b 0a 20 20 20 20 22 66 6f    "*": {.    "fo
6ef0: 73 73 69 6c 5f 73 65 72 76 65 72 22 3a 20 7b 0a  ssil_server": {.
6f00: 20 20 20 20 20 20 22 61 72 67 73 22 3a 20 5b 0a        "args": [.
6f10: 20 20 20 20 20 20 20 20 22 73 65 72 76 65 72 22          "server"
6f20: 2c 0a 20 20 20 20 20 20 20 20 22 2d 2d 72 65 70  ,.        "--rep
6f30: 6f 6c 69 73 74 22 2c 20 22 24 42 41 53 45 44 49  olist", "$BASEDI
6f40: 52 22 2c 0a 20 20 20 20 20 20 20 20 22 2d 2d 6e  R",.        "--n
6f50: 6f 6a 61 69 6c 22 2c 0a 20 20 20 20 20 20 20 20  ojail",.        
6f60: 22 2d 2d 65 72 72 6f 72 6c 6f 67 22 2c 20 22 24  "--errorlog", "$
6f70: 42 41 53 45 44 49 52 2f 66 6f 73 73 69 6c 2e 65  BASEDIR/fossil.e
6f80: 72 72 2e 6c 6f 67 22 0a 20 20 20 20 20 20 5d 0a  rr.log".      ].
6f90: 20 20 20 20 7d 2c 0a 20 20 20 20 22 73 79 6e 63      },.    "sync
6fa0: 22 3a 20 5b 0a 20 20 20 20 20 20 22 42 65 61 67  ": [.      "Beag
6fb0: 6c 65 42 61 64 67 65 22 2c 0a 20 20 20 20 20 20  leBadge",.      
6fc0: 22 61 72 6d 62 69 61 6e 2d 62 75 69 6c 64 22 2c  "armbian-build",
6fd0: 0a 20 20 20 20 20 20 22 62 61 64 67 65 2d 6c 61  .      "badge-la
6fe0: 75 6e 63 68 65 72 22 2c 0a 20 20 20 20 20 20 22  uncher",.      "
6ff0: 62 61 74 74 6c 65 73 6e 61 6b 65 2d 72 75 6c 65  battlesnake-rule
7000: 73 22 2c 0a 20 20 20 20 20 20 22 62 62 2d 69 6d  s",.      "bb-im
7010: 61 67 65 72 2d 72 73 22 2c 0a 20 20 20 20 20 20  ager-rs",.      
7020: 22 62 6c 61 62 6c 22 2c 0a 20 20 20 20 20 20 22  "blabl",.      "
7030: 62 6f 61 72 64 2d 67 65 6e 65 72 61 74 6f 72 22  board-generator"
7040: 2c 0a 20 20 20 20 20 20 22 62 6f 6f 74 73 74 72  ,.      "bootstr
7050: 61 70 2d 71 63 6f 77 32 22 2c 0a 20 20 20 20 20  ap-qcow2",.     
7060: 20 22 63 6c 65 61 76 65 22 2c 0a 20 20 20 20 20   "cleave",.     
7070: 20 22 63 72 79 73 74 61 6c 2d 62 6f 6f 74 73 74   "crystal-bootst
7080: 72 61 70 22 2c 0a 20 20 20 20 20 20 22 63 72 79  rap",.      "cry
7090: 73 74 61 6c 2d 72 6f 62 6f 74 73 22 2c 0a 20 20  stal-robots",.  
70a0: 20 20 20 20 22 63 72 79 73 74 61 6c 22 2c 0a 20      "crystal",. 
70b0: 20 20 20 20 20 22 66 6f 73 73 69 6c 2d 73 63 6d       "fossil-scm
70c0: 2e 6f 72 67 22 2c 0a 20 20 20 20 20 20 22 67 6c  .org",.      "gl
70d0: 69 6e 74 22 2c 0a 20 20 20 20 20 20 22 67 70 2d  int",.      "gp-
70e0: 63 72 79 73 74 61 6c 22 2c 0a 20 20 20 20 20 20  crystal",.      
70f0: 22 69 6e 74 65 72 61 63 74 6f 72 22 2c 0a 20 20  "interactor",.  
7100: 20 20 20 20 22 6a 6b 65 6d 62 65 64 64 65 64 2d      "jkembedded-
7110: 61 75 64 69 6f 2d 62 6f 61 72 64 22 2c 0a 20 20  audio-board",.  
7120: 20 20 20 20 22 6c 76 67 6c 2d 63 72 79 73 74 61      "lvgl-crysta
7130: 6c 22 2c 0a 20 20 20 20 20 20 22 6d 61 6b 65 2d  l",.      "make-
7140: 74 68 69 6e 67 73 2d 62 6f 6f 6b 22 2c 0a 20 20  things-book",.  
7150: 20 20 20 20 22 6d 65 63 68 61 6e 69 63 61 6c 2d      "mechanical-
7160: 74 75 72 6b 22 2c 0a 20 20 20 20 20 20 22 6d 69  turk",.      "mi
7170: 63 72 6f 62 6c 6f 63 6b 73 2d 73 6d 61 6c 6c 76  croblocks-smallv
7180: 6d 22 2c 0a 20 20 20 20 20 20 22 6f 6c 6c 61 6d  m",.      "ollam
7190: 61 22 2c 0a 20 20 20 20 20 20 22 71 75 61 64 65  a",.      "quade
71a0: 73 63 22 2c 0a 20 20 20 20 20 20 22 73 65 6d 61  sc",.      "sema
71b0: 6e 74 69 63 2d 75 69 22 2c 0a 20 20 20 20 20 20  ntic-ui",.      
71c0: 22 73 6f 63 69 61 6c 2d 62 61 64 67 65 22 2c 0a  "social-badge",.
71d0: 20 20 20 20 20 20 22 73 74 6f 6e 65 22 2c 0a 20        "stone",. 
71e0: 20 20 20 20 20 22 73 77 6b 2d 61 69 22 2c 0a 20       "swk-ai",. 
71f0: 20 20 20 20 20 22 74 6f 75 72 6e 61 6d 65 6e 74       "tournament
7200: 2d 74 6f 6f 6c 22 2c 0a 20 20 20 20 20 20 22 76  -tool",.      "v
7210: 73 78 2d 65 78 61 6d 70 6c 65 73 22 2c 0a 20 20  sx-examples",.  
7220: 20 20 20 20 22 77 65 62 69 64 65 2d 61 67 65 6e      "webide-agen
7230: 74 22 2c 0a 20 20 20 20 20 20 22 7a 65 70 74 6f  t",.      "zepto
7240: 22 0a 20 20 20 20 5d 0a 20 20 7d 2c 0a 20 20 22  ".    ].  },.  "
7250: 61 61 72 63 68 36 34 2d 31 22 3a 20 7b 0a 20 20  aarch64-1": {.  
7260: 20 20 22 6f 63 78 22 3a 20 7b 0a 20 20 20 20 20    "ocx": {.     
7270: 20 22 64 61 65 6d 6f 6e 5f 72 6f 6f 74 22 3a 20   "daemon_root": 
7280: 22 2f 6f 70 74 2f 6f 63 78 22 2c 0a 20 20 20 20  "/opt/ocx",.    
7290: 20 20 22 72 65 61 64 5f 77 72 69 74 65 22 3a 20    "read_write": 
72a0: 5b 22 2f 64 61 74 61 22 5d 0a 20 20 20 20 7d 0a  ["/data"].    }.
72b0: 20 20 7d 0a 7d 0a 00 00 00 00 00 00 00 00 00 00    }.}...........
72c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
72d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
72e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
72f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7310: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7320: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7350: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7360: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7390: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
73a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
73b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
73c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
73d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
73e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
73f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7400: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69  scripts-for-revi
7410: 65 77 2d 33 2f 72 65 63 6f 72 64 2e 74 78 74 00  ew-3/record.txt.
7420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7460: 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30  ....0000644.0000
7470: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30  000.0000000.0000
7480: 30 30 32 30 30 33 37 00 31 35 32 37 30 36 36 34  0020037.15270664
7490: 36 31 33 00 30 31 35 35 32 31 00 20 30 00 00 00  613.015521. 0...
74a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
74b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
74c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
74d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
74e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
74f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7500: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00  .ustar  .root...
7510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7520: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00  .........root...
7530: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7540: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7550: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7560: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7570: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7590: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
75a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
75b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
75c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
75d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
75e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
75f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7600: 0a 3d 3d 20 62 75 69 6c 64 3a 20 73 74 61 72 74  .== build: start
7610: 2d 6f 63 78 2c 20 72 65 63 65 69 70 74 73 20 61  -ocx, receipts a
7620: 6e 64 20 6f 63 78 32 20 66 72 6f 6d 20 2f 76 61  nd ocx2 from /va
7630: 72 2f 6c 69 62 2f 6f 63 78 2f 61 67 65 6e 74 73  r/lib/ocx/agents
7640: 2f 6f 70 75 73 2d 6f 63 78 32 2f 70 72 6f 6a 65  /opus-ocx2/proje
7650: 63 74 73 2f 6f 6c 6c 61 6d 61 2f 62 32 64 65 38  cts/ollama/b2de8
7660: 62 30 63 32 63 36 37 30 30 65 62 35 63 36 38 36  b0c2c6700eb5c686
7670: 62 31 37 34 37 62 35 61 61 61 31 65 32 64 32 64  b1747b5aaa1e2d2d
7680: 30 31 33 37 30 61 63 35 35 66 32 65 35 61 31 35  01370ac55f2e5a15
7690: 31 61 36 37 65 30 33 62 34 38 61 2f 74 72 65 65  1a67e03b48a/tree
76a0: 38 0a 0a 3d 3d 20 73 65 72 76 65 64 20 72 65 70  8..== served rep
76b0: 6f 73 69 74 6f 72 79 3a 20 64 65 6d 6f 2e 66 6f  ository: demo.fo
76c0: 73 73 69 6c 2c 20 75 73 65 72 73 2c 20 63 68 61  ssil, users, cha
76d0: 74 20 74 69 6d 65 6c 69 6e 65 0a 2d 72 77 2d 2d  t timeline.-rw--
76e0: 2d 2d 2d 2d 2d 20 20 20 20 31 20 72 6f 6f 74 20  -----    1 root 
76f0: 20 20 20 20 72 6f 6f 74 20 20 20 20 20 20 20 20      root        
7700: 32 31 37 30 38 38 20 4f 63 74 20 32 39 20 31 35  217088 Oct 29 15
7710: 3a 34 31 20 2f 74 6d 70 2f 73 41 2d 33 38 30 31  :41 /tmp/sA-3801
7720: 37 33 33 2f 66 6f 73 73 69 6c 2f 64 65 6d 6f 2e  733/fossil/demo.
7730: 66 6f 73 73 69 6c 0a 0a 3d 3d 20 72 65 63 65 69  fossil..== recei
7740: 70 74 20 73 65 72 76 69 63 65 2c 20 74 68 72 6f  pt service, thro
7750: 75 67 68 20 74 68 65 20 6c 61 75 6e 63 68 65 72  ugh the launcher
7760: 27 73 20 72 65 63 65 69 70 74 73 20 70 6f 6c 69  's receipts poli
7770: 63 79 0a 74 6f 74 61 6c 20 30 0a 73 72 77 2d 2d  cy.total 0.srw--
7780: 2d 2d 2d 2d 2d 20 20 20 20 31 20 72 6f 6f 74 20  -----    1 root 
7790: 20 20 20 20 72 6f 6f 74 20 20 20 20 20 20 20 20      root        
77a0: 20 20 20 20 20 30 20 4f 63 74 20 32 39 20 31 35       0 Oct 29 15
77b0: 3a 34 31 20 72 65 63 65 69 70 74 73 2e 73 6f 63  :41 receipts.soc
77c0: 6b 0a 0a 3d 3d 20 46 6f 73 73 69 6c 20 73 65 72  k..== Fossil ser
77d0: 76 65 72 2c 20 69 74 73 20 65 78 74 72 6f 6f 74  ver, its extroot
77e0: 27 73 20 6f 63 78 32 2d 63 67 69 20 74 68 65 20  's ocx2-cgi the 
77f0: 6c 61 75 6e 63 68 65 72 0a 0a 3d 3d 20 74 68 65  launcher..== the
7800: 20 64 61 65 6d 6f 6e 27 73 20 6e 6f 64 65 3a 20   daemon's node: 
7810: 6e 6f 64 65 73 2e 6a 73 6f 6e 20 61 6e 64 20 63  nodes.json and c
7820: 72 65 64 65 6e 74 69 61 6c 73 0a 0a 3d 3d 20 63  redentials..== c
7830: 6f 6e 66 69 6e 65 64 20 64 61 65 6d 6f 6e 2c 20  onfined daemon, 
7840: 74 68 72 6f 75 67 68 20 74 68 65 20 6c 61 75 6e  through the laun
7850: 63 68 65 72 27 73 20 64 61 65 6d 6f 6e 20 70 6f  cher's daemon po
7860: 6c 69 63 79 0a 7b 22 77 68 65 6e 22 3a 22 32 30  licy.{"when":"20
7870: 32 36 2d 31 30 2d 32 39 54 31 35 3a 34 32 3a 31  26-10-29T15:42:1
7880: 36 5a 22 2c 22 63 6c 6f 6e 65 22 3a 22 2f 74 6d  6Z","clone":"/tm
7890: 70 2f 73 41 2d 33 38 30 31 37 33 33 2f 64 61 65  p/sA-3801733/dae
78a0: 6d 6f 6e 2f 6e 6f 64 65 2f 63 6c 6f 6e 65 73 2f  mon/node/clones/
78b0: 64 65 6d 6f 2e 66 6f 73 73 69 6c 22 2c 22 63 6f  demo.fossil","co
78c0: 6d 70 6c 65 74 69 6f 6e 22 3a 31 2c 22 6e 65 77  mpletion":1,"new
78d0: 22 3a 30 2c 22 68 61 6e 64 65 64 22 3a 5b 5d 2c  ":0,"handed":[],
78e0: 22 73 65 74 74 6c 65 64 22 3a 30 7d 0a 7b 22 77  "settled":0}.{"w
78f0: 68 65 6e 22 3a 22 32 30 32 36 2d 31 30 2d 32 39  hen":"2026-10-29
7900: 54 31 35 3a 34 32 3a 31 39 5a 22 2c 22 63 6c 6f  T15:42:19Z","clo
7910: 6e 65 22 3a 22 2f 74 6d 70 2f 73 41 2d 33 38 30  ne":"/tmp/sA-380
7920: 31 37 33 33 2f 64 61 65 6d 6f 6e 2f 6e 6f 64 65  1733/daemon/node
7930: 2f 63 6c 6f 6e 65 73 2f 64 65 6d 6f 2e 66 6f 73  /clones/demo.fos
7940: 73 69 6c 22 2c 22 63 6f 6d 70 6c 65 74 69 6f 6e  sil","completion
7950: 22 3a 31 2c 22 6e 65 77 22 3a 30 2c 22 68 61 6e  ":1,"new":0,"han
7960: 64 65 64 22 3a 5b 5d 2c 22 73 65 74 74 6c 65 64  ded":[],"settled
7970: 22 3a 30 7d 0a 7b 22 77 68 65 6e 22 3a 22 32 30  ":0}.{"when":"20
7980: 32 36 2d 31 30 2d 32 39 54 31 35 3a 34 32 3a 32  26-10-29T15:42:2
7990: 32 5a 22 2c 22 63 6c 6f 6e 65 22 3a 22 2f 74 6d  2Z","clone":"/tm
79a0: 70 2f 73 41 2d 33 38 30 31 37 33 33 2f 64 61 65  p/sA-3801733/dae
79b0: 6d 6f 6e 2f 6e 6f 64 65 2f 63 6c 6f 6e 65 73 2f  mon/node/clones/
79c0: 64 65 6d 6f 2e 66 6f 73 73 69 6c 22 2c 22 63 6f  demo.fossil","co
79d0: 6d 70 6c 65 74 69 6f 6e 22 3a 31 2c 22 6e 65 77  mpletion":1,"new
79e0: 22 3a 30 2c 22 68 61 6e 64 65 64 22 3a 5b 5d 2c  ":0,"handed":[],
79f0: 22 73 65 74 74 6c 65 64 22 3a 30 7d 0a 0a 3d 3d  "settled":0}..==
7a00: 20 74 77 6f 20 72 75 6e 73 2c 20 70 6f 73 74 65   two runs, poste
7a10: 64 20 62 79 20 62 6f 73 73 20 28 73 65 74 75 70  d by boss (setup
7a20: 29 20 74 68 72 6f 75 67 68 20 46 6f 73 73 69 6c  ) through Fossil
7a30: 27 73 20 66 6f 72 6d 0a 73 61 6e 64 62 6f 78 65  's form.sandboxe
7a40: 64 20 72 75 6e 20 62 62 31 61 61 36 37 66 39 65  d run bb1aa67f9e
7a50: 0a 75 6e 63 6f 6e 66 69 6e 65 64 20 72 75 6e 20  .unconfined run 
7a60: 30 33 64 37 62 62 63 62 64 66 0a 0a 3d 3d 20 77  03d7bbcbdf..== w
7a70: 68 61 74 20 74 68 65 20 72 75 6e 73 20 70 6f 73  hat the runs pos
7a80: 74 65 64 20 28 73 74 61 72 74 65 64 2c 20 6b 65  ted (started, ke
7a90: 70 74 20 6c 6f 67 73 2c 20 65 6e 64 65 64 29 0a  pt logs, ended).
7aa0: 27 62 62 31 61 61 36 37 66 39 65 27 2c 27 62 6f  'bb1aa67f9e','bo
7ab0: 73 73 27 2c 27 44 20 32 30 32 36 2d 31 30 2d 32  ss','D 2026-10-2
7ac0: 39 54 31 35 3a 34 32 3a 32 34 2e 33 37 32 20 48  9T15:42:24.372 H
7ad0: 20 73 61 6e 64 62 6f 78 65 64 5c 73 70 72 6f 62   sandboxed\sprob
7ae0: 65 20 4e 20 74 65 78 74 2f 78 2d 6d 61 72 6b 64  e N text/x-markd
7af0: 6f 77 6e 20 55 20 62 6f 73 73 20 57 20 31 30 30  own U boss W 100
7b00: 37 20 60 60 60 6a 73 6f 6e 20 7b 22 6f 63 78 22  7 ```json {"ocx"
7b10: 3a 20 31 2c 20 22 6b 69 6e 64 22 3a 20 22 72 75  : 1, "kind": "ru
7b20: 6e 22 2c 20 22 6e 6f 64 65 22 3a 20 22 64 65 6d  n", "node": "dem
7b30: 6f 2d 6e 6f 64 65 22 2c 20 22 6f 62 6a 65 63 74  o-node", "object
7b40: 69 76 65 22 3a 20 22 50 72 6f 62 65 20 66 72 6f  ive": "Probe fro
7b50: 6d 20 61 6e 20 61 67 65 6e 74 20 73 61 6e 64 62  m an agent sandb
7b60: 6f 78 2e 22 2c 20 22 77 6f 72 6b 73 70 61 63 65  ox.", "workspace
7b70: 22 3a 20 5b 7b 22 73 74 65 70 22 3a 20 22 64 69  ": [{"step": "di
7b80: 72 65 63 74 6f 72 79 22 2c 20 22 61 74 22 3a 20  rectory", "at": 
7b90: 22 24 24 72 6f 6f 74 24 24 2f 24 24 75 73 65 72  "$$root$$/$$user
7ba0: 24 24 2f 70 72 6f 6a 65 63 74 73 2f 24 24 70 72  $$/projects/$$pr
7bb0: 6f 6a 65 63 74 24 24 2f 24 24 72 75 6e 24 24 22  oject$$/$$run$$"
7bc0: 7d 5d 2c 20 22 73 61 6e 64 62 6f 78 22 3a 20 7b  }], "sandbox": {
7bd0: 22 75 69 64 22 3a 20 31 30 30 30 2c 20 22 67 69  "uid": 1000, "gi
7be0: 64 22 3a 20 31 30 30 30 2c 20 22 63 61 70 61 62  d": 1000, "capab
7bf0: 69 6c 69 74 69 65 73 22 3a 20 5b 5d 2c 20 22 6e  ilities": [], "n
7c00: 65 74 77 6f 72 6b 22 3a 20 66 61 6c 73 65 2c 20  etwork": false, 
7c10: 22 6f 77 6e 5f 73 65 73 73 69 6f 6e 22 3a 20 74  "own_session": t
7c20: 72 75 65 2c 20 22 65 6e 64 73 5f 77 69 74 68 5f  rue, "ends_with_
7c30: 6e 6f 64 65 22 3a 20 74 72 75 65 2c 20 22 72 65  node": true, "re
7c40: 61 64 5f 6f 6e 6c 79 22 3a 20 5b 22 2f 75 73 72  ad_only": ["/usr
7c50: 22 2c 20 22 2f 62 69 6e 22 2c 20 22 2f 6c 69 62  ", "/bin", "/lib
7c60: 22 2c 20 22 2f 6c 69 62 36 34 22 5d 2c 20 22 72  ", "/lib64"], "r
7c70: 65 61 64 5f 77 72 69 74 65 22 3a 20 5b 22 24 24  ead_write": ["$$
7c80: 77 6f 72 6b 73 70 61 63 65 24 24 22 5d 2c 20 22  workspace$$"], "
7c90: 68 6f 6d 65 73 22 3a 20 5b 5d 2c 20 22 66 69 6c  homes": [], "fil
7ca0: 65 73 22 3a 20 5b 5d 2c 20 22 70 72 6f 63 22 3a  es": [], "proc":
7cb0: 20 22 2f 70 72 6f 63 22 2c 20 22 64 65 76 22 3a   "/proc", "dev":
7cc0: 20 22 2f 64 65 76 22 2c 20 22 74 6d 70 22 3a 20   "/dev", "tmp": 
7cd0: 22 2f 74 6d 70 22 7d 2c 20 22 6c 69 6d 69 74 73  "/tmp"}, "limits
7ce0: 22 3a 20 7b 22 77 61 6c 6c 22 3a 20 22 32 6d 22  ": {"wall": "2m"
7cf0: 7d 2c 20 22 70 72 6f 63 65 73 73 22 3a 20 7b 22  }, "process": {"
7d00: 75 73 65 72 22 3a 20 22 62 75 69 6c 64 27 0a 27  user": "build'.'
7d10: 30 33 64 37 62 62 63 62 64 66 27 2c 27 62 6f 73  03d7bbcbdf','bos
7d20: 73 27 2c 27 44 20 32 30 32 36 2d 31 30 2d 32 39  s','D 2026-10-29
7d30: 54 31 35 3a 34 32 3a 32 34 2e 34 37 39 20 48 20  T15:42:24.479 H 
7d40: 75 6e 63 6f 6e 66 69 6e 65 64 5c 73 70 72 6f 62  unconfined\sprob
7d50: 65 20 4e 20 74 65 78 74 2f 78 2d 6d 61 72 6b 64  e N text/x-markd
7d60: 6f 77 6e 20 55 20 62 6f 73 73 20 57 20 36 34 32  own U boss W 642
7d70: 20 60 60 60 6a 73 6f 6e 20 7b 22 6f 63 78 22 3a   ```json {"ocx":
7d80: 20 31 2c 20 22 6b 69 6e 64 22 3a 20 22 72 75 6e   1, "kind": "run
7d90: 22 2c 20 22 6e 6f 64 65 22 3a 20 22 64 65 6d 6f  ", "node": "demo
7da0: 2d 6e 6f 64 65 22 2c 20 22 6f 62 6a 65 63 74 69  -node", "objecti
7db0: 76 65 22 3a 20 22 50 72 6f 62 65 20 66 72 6f 6d  ve": "Probe from
7dc0: 20 74 68 65 20 64 61 65 6d 6f 6e 20 69 74 73 65   the daemon itse
7dd0: 6c 66 2c 20 75 6e 63 6f 6e 66 69 6e 65 64 20 62  lf, unconfined b
7de0: 79 20 61 6e 79 20 72 75 6e 20 73 61 6e 64 62 6f  y any run sandbo
7df0: 78 2e 22 2c 20 22 70 72 6f 63 65 73 73 22 3a 20  x.", "process": 
7e00: 7b 22 75 73 65 72 22 3a 20 22 62 75 69 6c 64 65  {"user": "builde
7e10: 72 22 2c 20 22 63 6f 6d 6d 61 6e 64 22 3a 20 5b  r", "command": [
7e20: 22 2f 62 69 6e 2f 73 68 22 2c 20 22 2d 63 22 2c  "/bin/sh", "-c",
7e30: 20 22 65 63 68 6f 20 75 69 64 3d 24 28 69 64 20   "echo uid=$(id 
7e40: 2d 75 29 3b 20 66 6f 72 20 70 20 69 6e 20 2f 74  -u); for p in /t
7e50: 6d 70 2f 73 41 2d 33 38 30 31 37 33 33 2f 66 6f  mp/sA-3801733/fo
7e60: 73 73 69 6c 20 2f 74 6d 70 2f 73 41 2d 33 38 30  ssil /tmp/sA-380
7e70: 31 37 33 33 2f 66 6f 73 73 69 6c 2f 64 65 6d 6f  1733/fossil/demo
7e80: 2e 66 6f 73 73 69 6c 3b 20 64 6f 20 5b 20 2d 65  .fossil; do [ -e
7e90: 20 24 70 20 5d 20 26 26 20 65 63 68 6f 20 56 49   $p ] && echo VI
7ea0: 53 49 42 4c 45 20 24 70 20 7c 7c 20 65 63 68 6f  SIBLE $p || echo
7eb0: 20 61 62 73 65 6e 74 20 24 70 3b 20 64 6f 6e 65   absent $p; done
7ec0: 3b 20 68 65 61 64 20 2d 63 20 31 36 20 2f 74 6d  ; head -c 16 /tm
7ed0: 70 2f 73 41 2d 33 38 30 31 37 33 33 2f 66 6f 73  p/sA-3801733/fos
7ee0: 73 69 6c 2f 64 65 6d 6f 2e 66 6f 73 73 69 6c 20  sil/demo.fossil 
7ef0: 3e 2f 64 65 76 2f 6e 75 6c 6c 20 32 3e 26 31 20  >/dev/null 2>&1 
7f00: 26 26 20 65 63 68 6f 20 52 45 41 44 2d 4f 4b 20  && echo READ-OK 
7f10: 7c 7c 20 65 63 68 6f 20 72 65 61 64 2d 72 65 66  || echo read-ref
7f20: 75 73 65 64 3b 20 6e 3d 30 3b 20 66 6f 72 20 78  used; n=0; for x
7f30: 20 69 6e 20 2f 70 72 6f 63 2f 5b 30 2d 39 5d 2a   in /proc/[0-9]*
7f40: 2f 72 6f 6f 74 2f 74 6d 70 2f 73 41 2d 33 38 30  /root/tmp/sA-380
7f50: 31 37 33 33 2f 66 6f 73 73 69 6c 2f 64 65 6d 6f  1733/fossil/demo
7f60: 2e 66 6f 73 73 69 6c 3b 20 64 6f 20 5b 20 2d 72  .fossil; do [ -r
7f70: 20 24 78 20 5d 20 26 26 20 6e 3d 24 27 0a 27 65   $x ] && n=$'.'e
7f80: 36 65 66 36 31 36 33 30 30 27 2c 27 64 65 6d 6f  6ef616300','demo
7f90: 2d 6e 6f 64 65 27 2c 27 44 20 32 30 32 36 2d 31  -node','D 2026-1
7fa0: 30 2d 32 39 54 31 35 3a 34 32 3a 32 35 2e 36 39  0-29T15:42:25.69
7fb0: 36 20 47 20 62 62 31 61 61 36 37 66 39 65 66 30  6 G bb1aa67f9ef0
7fc0: 35 63 31 61 33 64 66 64 38 35 37 36 61 34 37 38  5c1a3dfd8576a478
7fd0: 30 37 36 61 31 63 32 62 61 65 31 31 36 34 33 63  076a1c2bae11643c
7fe0: 31 36 62 37 63 34 33 65 38 33 35 36 62 31 33 64  16b7c43e8356b13d
7ff0: 31 61 63 63 20 49 20 62 62 31 61 61 36 37 66 39  1acc I bb1aa67f9
8000: 65 66 30 35 63 31 61 33 64 66 64 38 35 37 36 61  ef05c1a3dfd8576a
8010: 34 37 38 30 37 36 61 31 63 32 62 61 65 31 31 36  478076a1c2bae116
8020: 34 33 63 31 36 62 37 63 34 33 65 38 33 35 36 62  43c16b7c43e8356b
8030: 31 33 64 31 61 63 63 20 4e 20 74 65 78 74 2f 78  13d1acc N text/x
8040: 2d 6d 61 72 6b 64 6f 77 6e 20 55 20 64 65 6d 6f  -markdown U demo
8050: 2d 6e 6f 64 65 20 57 20 36 38 37 20 60 60 60 6a  -node W 687 ```j
8060: 73 6f 6e 20 7b 20 20 22 6f 63 78 22 3a 20 31 2c  son {  "ocx": 1,
8070: 20 20 22 6b 69 6e 64 22 3a 20 22 73 74 61 72 74    "kind": "start
8080: 65 64 22 2c 20 20 22 6e 6f 64 65 22 3a 20 22 64  ed",  "node": "d
8090: 65 6d 6f 2d 6e 6f 64 65 22 2c 20 20 22 63 6c 6f  emo-node",  "clo
80a0: 6e 65 22 3a 20 22 37 31 38 34 36 64 30 33 39 37  ne": "71846d0397
80b0: 30 66 32 38 62 30 33 31 39 35 30 62 38 61 66 61  0f28b031950b8afa
80c0: 39 62 30 64 63 32 31 35 38 34 38 61 62 38 22 2c  9b0dc215848ab8",
80d0: 20 20 22 72 75 6e 22 3a 20 22 62 62 31 61 61 36    "run": "bb1aa6
80e0: 37 66 39 65 66 30 35 63 31 61 33 64 66 64 38 35  7f9ef05c1a3dfd85
80f0: 37 36 61 34 37 38 30 37 36 61 31 63 32 62 61 65  76a478076a1c2bae
8100: 31 31 36 34 33 63 31 36 62 37 63 34 33 65 38 33  11643c16b7c43e83
8110: 35 36 62 31 33 64 31 61 63 63 22 2c 20 20 22 76  56b13d1acc",  "v
8120: 61 6c 75 65 73 22 3a 20 7b 20 20 20 22 72 6f 6f  alues": {   "roo
8130: 74 22 3a 20 22 2f 74 6d 70 2f 73 41 2d 33 38 30  t": "/tmp/sA-380
8140: 31 37 33 33 2f 64 61 65 6d 6f 6e 2f 61 67 65 6e  1733/daemon/agen
8150: 74 73 22 2c 20 20 20 22 70 72 6f 6a 65 63 74 22  ts",   "project"
8160: 3a 20 22 64 65 6d 6f 22 2c 20 20 20 22 75 73 65  : "demo",   "use
8170: 72 22 3a 20 22 62 75 69 6c 64 65 72 22 2c 20 20  r": "builder",  
8180: 20 22 72 75 6e 22 3a 20 22 62 62 31 61 61 36 37   "run": "bb1aa67
8190: 66 39 65 66 30 35 63 31 61 33 64 66 64 38 35 37  f9ef05c1a3dfd857
81a0: 36 61 34 37 38 30 37 36 61 31 63 32 62 61 65 31  6a478076a1c2bae1
81b0: 31 36 34 33 63 31 36 62 37 63 34 33 65 38 33 35  1643c16b7c43e835
81c0: 36 62 31 33 64 31 61 63 63 22 2c 20 20 20 22 6e  6b13d1acc",   "n
81d0: 6f 64 65 22 3a 20 22 64 65 6d 6f 2d 6e 6f 64 65  ode": "demo-node
81e0: 22 2c 20 20 20 22 73 65 72 76 65 72 22 3a 20 22  ",   "server": "
81f0: 27 0a 27 61 61 30 37 65 35 30 38 32 38 27 2c 27  '.'aa07e50828','
8200: 64 65 6d 6f 2d 6e 6f 64 65 27 2c 27 44 20 32 30  demo-node','D 20
8210: 32 36 2d 31 30 2d 32 39 54 31 35 3a 34 32 3a 32  26-10-29T15:42:2
8220: 35 2e 38 39 31 20 47 20 62 62 31 61 61 36 37 66  5.891 G bb1aa67f
8230: 39 65 66 30 35 63 31 61 33 64 66 64 38 35 37 36  9ef05c1a3dfd8576
8240: 61 34 37 38 30 37 36 61 31 63 32 62 61 65 31 31  a478076a1c2bae11
8250: 36 34 33 63 31 36 62 37 63 34 33 65 38 33 35 36  643c16b7c43e8356
8260: 62 31 33 64 31 61 63 63 20 49 20 62 62 31 61 61  b13d1acc I bb1aa
8270: 36 37 66 39 65 66 30 35 63 31 61 33 64 66 64 38  67f9ef05c1a3dfd8
8280: 35 37 36 61 34 37 38 30 37 36 61 31 63 32 62 61  576a478076a1c2ba
8290: 65 31 31 36 34 33 63 31 36 62 37 63 34 33 65 38  e11643c16b7c43e8
82a0: 33 35 36 62 31 33 64 31 61 63 63 20 4e 20 74 65  356b13d1acc N te
82b0: 78 74 2f 78 2d 6d 61 72 6b 64 6f 77 6e 20 55 20  xt/x-markdown U 
82c0: 64 65 6d 6f 2d 6e 6f 64 65 20 57 20 32 33 32 20  demo-node W 232 
82d0: 54 68 65 20 70 72 6f 63 65 73 73 20 66 6f 72 20  The process for 
82e0: 74 68 65 20 72 75 6e 20 62 62 31 61 61 36 37 66  the run bb1aa67f
82f0: 39 65 66 30 35 63 31 61 33 64 66 64 38 35 37 36  9ef05c1a3dfd8576
8300: 61 34 37 38 30 37 36 61 31 63 32 62 61 65 31 31  a478076a1c2bae11
8310: 36 34 33 63 31 36 62 37 63 34 33 65 38 33 35 36  643c16b7c43e8356
8320: 62 31 33 64 31 61 63 63 20 65 78 69 74 65 64 20  b13d1acc exited 
8330: 30 2e 20 7e 7e 7e 7e 20 73 74 64 6f 75 74 20 75  0. ~~~~ stdout u
8340: 69 64 3d 31 30 30 30 20 61 62 73 65 6e 74 20 2f  id=1000 absent /
8350: 74 6d 70 2f 73 41 2d 33 38 30 31 37 33 33 2f 66  tmp/sA-3801733/f
8360: 6f 73 73 69 6c 20 61 62 73 65 6e 74 20 2f 74 6d  ossil absent /tm
8370: 70 2f 73 41 2d 33 38 30 31 37 33 33 2f 66 6f 73  p/sA-3801733/fos
8380: 73 69 6c 2f 64 65 6d 6f 2e 66 6f 73 73 69 6c 20  sil/demo.fossil 
8390: 72 65 61 64 2d 72 65 66 75 73 65 64 20 70 72 6f  read-refused pro
83a0: 63 2d 70 61 74 68 73 2d 72 65 61 64 61 62 6c 65  c-paths-readable
83b0: 3d 30 20 7e 7e 7e 7e 20 5a 20 33 30 63 32 33 35  =0 ~~~~ Z 30c235
83c0: 64 65 61 34 34 65 35 31 35 39 35 65 61 62 64 30  dea44e51595eabd0
83d0: 35 37 64 65 38 37 62 65 31 64 20 27 0a 27 39 62  57de87be1d '.'9b
83e0: 37 61 65 62 35 37 36 36 27 2c 27 64 65 6d 6f 2d  7aeb5766','demo-
83f0: 6e 6f 64 65 27 2c 27 44 20 32 30 32 36 2d 31 30  node','D 2026-10
8400: 2d 32 39 54 31 35 3a 34 32 3a 32 35 2e 39 30 31  -29T15:42:25.901
8410: 20 47 20 62 62 31 61 61 36 37 66 39 65 66 30 35   G bb1aa67f9ef05
8420: 63 31 61 33 64 66 64 38 35 37 36 61 34 37 38 30  c1a3dfd8576a4780
8430: 37 36 61 31 63 32 62 61 65 31 31 36 34 33 63 31  76a1c2bae11643c1
8440: 36 62 37 63 34 33 65 38 33 35 36 62 31 33 64 31  6b7c43e8356b13d1
8450: 61 63 63 20 49 20 62 62 31 61 61 36 37 66 39 65  acc I bb1aa67f9e
8460: 66 30 35 63 31 61 33 64 66 64 38 35 37 36 61 34  f05c1a3dfd8576a4
8470: 37 38 30 37 36 61 31 63 32 62 61 65 31 31 36 34  78076a1c2bae1164
8480: 33 63 31 36 62 37 63 34 33 65 38 33 35 36 62 31  3c16b7c43e8356b1
8490: 33 64 31 61 63 63 20 4e 20 74 65 78 74 2f 78 2d  3d1acc N text/x-
84a0: 6d 61 72 6b 64 6f 77 6e 20 55 20 64 65 6d 6f 2d  markdown U demo-
84b0: 6e 6f 64 65 20 57 20 32 33 36 20 60 60 60 6a 73  node W 236 ```js
84c0: 6f 6e 20 7b 20 20 22 6f 63 78 22 3a 20 31 2c 20  on {  "ocx": 1, 
84d0: 20 22 6b 69 6e 64 22 3a 20 22 65 6e 64 65 64 22   "kind": "ended"
84e0: 2c 20 20 22 6e 6f 64 65 22 3a 20 22 64 65 6d 6f  ,  "node": "demo
84f0: 2d 6e 6f 64 65 22 2c 20 20 22 63 6c 6f 6e 65 22  -node",  "clone"
8500: 3a 20 22 37 31 38 34 36 64 30 33 39 37 30 66 32  : "71846d03970f2
8510: 38 62 30 33 31 39 35 30 62 38 61 66 61 39 62 30  8b031950b8afa9b0
8520: 64 63 32 31 35 38 34 38 61 62 38 22 2c 20 20 22  dc215848ab8",  "
8530: 72 75 6e 22 3a 20 22 62 62 31 61 61 36 37 66 39  run": "bb1aa67f9
8540: 65 66 30 35 63 31 61 33 64 66 64 38 35 37 36 61  ef05c1a3dfd8576a
8550: 34 37 38 30 37 36 61 31 63 32 62 61 65 31 31 36  478076a1c2bae116
8560: 34 33 63 31 36 62 37 63 34 33 65 38 33 35 36 62  43c16b7c43e8356b
8570: 31 33 64 31 61 63 63 22 2c 20 20 22 6f 75 74 63  13d1acc",  "outc
8580: 6f 6d 65 22 3a 20 22 65 78 69 74 65 64 22 2c 20  ome": "exited", 
8590: 20 22 65 78 69 74 22 3a 20 30 20 7d 20 60 60 60   "exit": 0 } ```
85a0: 20 5a 20 33 61 62 31 34 62 34 65 31 65 64 61 62   Z 3ab14b4e1edab
85b0: 39 36 62 36 65 62 38 64 35 34 37 32 38 63 36 30  96b6eb8d54728c60
85c0: 32 65 64 20 27 0a 27 32 30 65 32 61 33 37 37 31  2ed '.'20e2a3771
85d0: 38 27 2c 27 64 65 6d 6f 2d 6e 6f 64 65 27 2c 27  8','demo-node','
85e0: 44 20 32 30 32 36 2d 31 30 2d 32 39 54 31 35 3a  D 2026-10-29T15:
85f0: 34 32 3a 32 36 2e 30 37 35 20 47 20 30 33 64 37  42:26.075 G 03d7
8600: 62 62 63 62 64 66 38 32 63 31 62 66 66 65 31 37  bbcbdf82c1bffe17
8610: 39 63 31 33 65 30 63 61 62 36 33 63 33 36 31 36  9c13e0cab63c3616
8620: 32 30 35 39 31 35 64 34 62 35 62 36 65 35 30 34  205915d4b5b6e504
8630: 37 66 30 30 37 66 62 33 33 64 34 63 20 49 20 30  7f007fb33d4c I 0
8640: 33 64 37 62 62 63 62 64 66 38 32 63 31 62 66 66  3d7bbcbdf82c1bff
8650: 65 31 37 39 63 31 33 65 30 63 61 62 36 33 63 33  e179c13e0cab63c3
8660: 36 31 36 32 30 35 39 31 35 64 34 62 35 62 36 65  616205915d4b5b6e
8670: 35 30 34 37 66 30 30 37 66 62 33 33 64 34 63 20  5047f007fb33d4c 
8680: 4e 20 74 65 78 74 2f 78 2d 6d 61 72 6b 64 6f 77  N text/x-markdow
8690: 6e 20 55 20 64 65 6d 6f 2d 6e 6f 64 65 20 57 20  n U demo-node W 
86a0: 32 37 37 20 60 60 60 6a 73 6f 6e 20 7b 20 20 22  277 ```json {  "
86b0: 6f 63 78 22 3a 20 31 2c 20 20 22 6b 69 6e 64 22  ocx": 1,  "kind"
86c0: 3a 20 22 65 6e 64 65 64 22 2c 20 20 22 6e 6f 64  : "ended",  "nod
86d0: 65 22 3a 20 22 64 65 6d 6f 2d 6e 6f 64 65 22 2c  e": "demo-node",
86e0: 20 20 22 63 6c 6f 6e 65 22 3a 20 22 37 31 38 34    "clone": "7184
86f0: 36 64 30 33 39 37 30 66 32 38 62 30 33 31 39 35  6d03970f28b03195
8700: 30 62 38 61 66 61 39 62 30 64 63 32 31 35 38 34  0b8afa9b0dc21584
8710: 38 61 62 38 22 2c 20 20 22 72 75 6e 22 3a 20 22  8ab8",  "run": "
8720: 30 33 64 37 62 62 63 62 64 66 38 32 63 31 62 66  03d7bbcbdf82c1bf
8730: 66 65 31 37 39 63 31 33 65 30 63 61 62 36 33 63  fe179c13e0cab63c
8740: 33 36 31 36 32 30 35 39 31 35 64 34 62 35 62 36  3616205915d4b5b6
8750: 65 35 30 34 37 66 30 30 37 66 62 33 33 64 34 63  e5047f007fb33d4c
8760: 22 2c 20 20 22 6f 75 74 63 6f 6d 65 22 3a 20 22  ",  "outcome": "
8770: 72 65 66 75 73 65 64 22 2c 20 20 22 6e 6f 74 65  refused",  "note
8780: 22 3a 20 22 74 68 69 73 20 6e 6f 64 65 20 72 75  ": "this node ru
8790: 6e 73 20 6f 6e 6c 79 20 73 61 6e 64 62 6f 78 65  ns only sandboxe
87a0: 64 20 70 72 6f 63 65 73 73 65 73 22 20 7d 20 60  d processes" } `
87b0: 60 60 20 5a 20 30 64 38 33 37 64 36 63 64 30 31  `` Z 0d837d6cd01
87c0: 37 62 62 31 61 31 38 30 31 64 64 62 63 62 38 32  7bb1a1801ddbcb82
87d0: 61 33 66 38 35 20 27 0a 0a 3d 3d 20 72 65 63 65  a3f85 '..== rece
87e0: 69 70 74 73 20 74 68 72 6f 75 67 68 20 74 68 65  ipts through the
87f0: 20 6c 61 75 6e 63 68 65 72 27 73 20 43 47 49 20   launcher's CGI 
8800: 72 6f 75 74 65 2c 20 61 67 61 69 6e 73 74 20 46  route, against F
8810: 6f 73 73 69 6c 27 73 20 6f 77 6e 20 72 65 63 6f  ossil's own reco
8820: 72 64 0a 62 6f 73 73 20 7b 22 61 72 74 69 66 61  rd.boss {"artifa
8830: 63 74 73 22 3a 20 5b 7b 22 61 72 74 69 66 61 63  cts": [{"artifac
8840: 74 22 3a 20 22 62 62 31 61 61 36 37 66 39 65 66  t": "bb1aa67f9ef
8850: 30 35 63 31 61 33 64 66 64 38 35 37 36 61 34 37  05c1a3dfd8576a47
8860: 38 30 37 36 61 31 63 32 62 61 65 31 31 36 34 33  8076a1c2bae11643
8870: 63 31 36 62 37 63 34 33 65 38 33 35 36 62 31 33  c16b7c43e8356b13
8880: 64 31 61 63 63 22 2c 20 22 72 65 63 65 69 76 65  d1acc", "receive
8890: 64 22 3a 20 74 72 75 65 2c 20 22 73 65 6e 64 65  d": true, "sende
88a0: 72 22 3a 20 22 62 6f 73 73 22 2c 20 22 63 61 70  r": "boss", "cap
88b0: 61 62 69 6c 69 74 69 65 73 22 3a 20 22 62 63 64  abilities": "bcd
88c0: 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74  efghijklmnopqrst
88d0: 77 7a 32 33 34 35 36 37 41 44 22 2c 20 22 72 65  wz234567AD", "re
88e0: 63 65 69 70 74 22 3a 20 32 2c 20 22 72 69 64 22  ceipt": 2, "rid"
88f0: 3a 20 32 7d 5d 2c 20 22 75 73 65 72 22 3a 20 7b  : 2}], "user": {
8900: 22 75 73 65 72 22 3a 20 22 64 65 6d 6f 2d 6e 6f  "user": "demo-no
8910: 64 65 22 2c 20 22 63 61 70 61 62 69 6c 69 74 69  de", "capabiliti
8920: 65 73 22 3a 20 22 67 68 6a 6f 72 77 7a 34 43 22  es": "ghjorwz4C"
8930: 2c 20 22 77 69 6b 69 5f 6d 6f 64 65 72 61 74 65  , "wiki_moderate
8940: 64 22 3a 20 66 61 6c 73 65 2c 20 22 74 69 63 6b  d": false, "tick
8950: 65 74 5f 6d 6f 64 65 72 61 74 65 64 22 3a 20 66  et_moderated": f
8960: 61 6c 73 65 7d 7d 0a 76 69 65 77 65 72 20 7b 22  alse}}.viewer {"
8970: 61 72 74 69 66 61 63 74 73 22 3a 20 5b 7b 22 61  artifacts": [{"a
8980: 72 74 69 66 61 63 74 22 3a 20 22 62 62 31 61 61  rtifact": "bb1aa
8990: 36 37 66 39 65 66 30 35 63 31 61 33 64 66 64 38  67f9ef05c1a3dfd8
89a0: 35 37 36 61 34 37 38 30 37 36 61 31 63 32 62 61  576a478076a1c2ba
89b0: 65 31 31 36 34 33 63 31 36 62 37 63 34 33 65 38  e11643c16b7c43e8
89c0: 33 35 36 62 31 33 64 31 61 63 63 22 2c 20 22 72  356b13d1acc", "r
89d0: 65 63 65 69 76 65 64 22 3a 20 74 72 75 65 2c 20  eceived": true, 
89e0: 22 73 65 6e 64 65 72 22 3a 20 22 62 6f 73 73 22  "sender": "boss"
89f0: 2c 20 22 63 61 70 61 62 69 6c 69 74 69 65 73 22  , "capabilities"
8a00: 3a 20 22 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e  : "bcdefghijklmn
8a10: 6f 70 71 72 73 74 77 7a 32 33 34 35 36 37 41 44  opqrstwz234567AD
8a20: 22 2c 20 22 72 65 63 65 69 70 74 22 3a 20 32 2c  ", "receipt": 2,
8a30: 20 22 72 69 64 22 3a 20 32 7d 5d 2c 20 22 75 73   "rid": 2}], "us
8a40: 65 72 22 3a 20 7b 22 75 73 65 72 22 3a 20 22 64  er": {"user": "d
8a50: 65 6d 6f 2d 6e 6f 64 65 22 2c 20 22 63 61 70 61  emo-node", "capa
8a60: 62 69 6c 69 74 69 65 73 22 3a 20 22 67 68 6a 6f  bilities": "ghjo
8a70: 72 77 7a 34 43 22 2c 20 22 77 69 6b 69 5f 6d 6f  rwz4C", "wiki_mo
8a80: 64 65 72 61 74 65 64 22 3a 20 66 61 6c 73 65 2c  derated": false,
8a90: 20 22 74 69 63 6b 65 74 5f 6d 6f 64 65 72 61 74   "ticket_moderat
8aa0: 65 64 22 3a 20 66 61 6c 73 65 7d 7d 0a 61 6e 6f  ed": false}}.ano
8ab0: 6e 20 7b 22 61 72 74 69 66 61 63 74 73 22 3a 20  n {"artifacts": 
8ac0: 5b 7b 22 61 72 74 69 66 61 63 74 22 3a 20 22 62  [{"artifact": "b
8ad0: 62 31 61 61 36 37 66 39 65 66 30 35 63 31 61 33  b1aa67f9ef05c1a3
8ae0: 64 66 64 38 35 37 36 61 34 37 38 30 37 36 61 31  dfd8576a478076a1
8af0: 63 32 62 61 65 31 31 36 34 33 63 31 36 62 37 63  c2bae11643c16b7c
8b00: 34 33 65 38 33 35 36 62 31 33 64 31 61 63 63 22  43e8356b13d1acc"
8b10: 2c 20 22 72 65 63 65 69 76 65 64 22 3a 20 74 72  , "received": tr
8b20: 75 65 2c 20 22 73 65 6e 64 65 72 22 3a 20 22 62  ue, "sender": "b
8b30: 6f 73 73 22 2c 20 22 63 61 70 61 62 69 6c 69 74  oss", "capabilit
8b40: 69 65 73 22 3a 20 22 62 63 64 65 66 67 68 69 6a  ies": "bcdefghij
8b50: 6b 6c 6d 6e 6f 70 71 72 73 74 77 7a 32 33 34 35  klmnopqrstwz2345
8b60: 36 37 41 44 22 2c 20 22 72 65 63 65 69 70 74 22  67AD", "receipt"
8b70: 3a 20 32 2c 20 22 72 69 64 22 3a 20 32 7d 5d 2c  : 2, "rid": 2}],
8b80: 20 22 75 73 65 72 22 3a 20 7b 22 75 73 65 72 22   "user": {"user"
8b90: 3a 20 22 64 65 6d 6f 2d 6e 6f 64 65 22 2c 20 22  : "demo-node", "
8ba0: 63 61 70 61 62 69 6c 69 74 69 65 73 22 3a 20 22  capabilities": "
8bb0: 67 68 6a 6f 72 77 7a 34 43 22 2c 20 22 77 69 6b  ghjorwz4C", "wik
8bc0: 69 5f 6d 6f 64 65 72 61 74 65 64 22 3a 20 66 61  i_moderated": fa
8bd0: 6c 73 65 2c 20 22 74 69 63 6b 65 74 5f 6d 6f 64  lse, "ticket_mod
8be0: 65 72 61 74 65 64 22 3a 20 66 61 6c 73 65 7d 7d  erated": false}}
8bf0: 0a 46 6f 73 73 69 6c 27 73 20 6f 77 6e 20 72 65  .Fossil's own re
8c00: 63 6f 72 64 3a 20 27 62 6f 73 73 27 2c 27 62 63  cord: 'boss','bc
8c10: 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73  defghijklmnopqrs
8c20: 74 77 7a 32 33 34 35 36 37 41 44 27 2c 32 2c 32  twz234567AD',2,2
8c30: 0a 46 6f 73 73 69 6c 27 73 20 66 75 6c 6c 63 61  .Fossil's fullca
8c40: 70 28 64 65 6d 6f 2d 6e 6f 64 65 29 3a 20 27 67  p(demo-node): 'g
8c50: 68 6a 6f 72 77 7a 34 43 27 0a 0a 3d 3d 20 74 68  hjorwz4C'..== th
8c60: 65 20 63 6f 6e 73 6f 6c 65 2c 20 61 73 20 62 6f  e console, as bo
8c70: 73 73 20 28 74 68 65 20 70 61 67 65 27 73 20 6f  ss (the page's o
8c80: 74 68 65 72 20 70 61 74 68 73 20 73 74 69 6c 6c  ther paths still
8c90: 20 70 61 73 73 20 74 68 72 6f 75 67 68 3a 20 45   pass through: E
8ca0: 36 29 0a 63 6f 6e 73 6f 6c 65 20 6f 6b 3a 20 54  6).console ok: T
8cb0: 72 75 65 20 7c 20 72 75 6e 73 20 72 75 6e 6e 69  rue | runs runni
8cc0: 6e 67 3a 20 30 20 7c 20 65 6e 64 65 64 3a 20 32  ng: 0 | ended: 2
8cd0: 0a 0a 3d 3d 20 77 72 69 74 65 73 20 66 72 6f 6d  ..== writes from
8ce0: 20 74 68 65 20 72 65 63 65 69 70 74 20 73 65 72   the receipt ser
8cf0: 76 69 63 65 27 73 20 70 6f 6c 69 63 79 20 66 61  vice's policy fa
8d00: 69 6c 20 28 61 20 70 72 6f 62 65 20 69 6e 20 70  il (a probe in p
8d10: 6c 61 63 65 20 6f 66 20 72 65 63 65 69 70 74 73  lace of receipts
8d20: 29 0a 46 6f 73 73 69 6c 27 73 20 70 6f 72 74 20  ).Fossil's port 
8d30: 35 37 31 32 31 20 61 6e 73 77 65 72 73 20 6f 6e  57121 answers on
8d40: 20 74 68 65 20 68 6f 73 74 3a 20 32 30 30 0a 47   the host: 200.G
8d50: 43 20 57 61 72 6e 69 6e 67 3a 20 43 6f 75 6c 64  C Warning: Could
8d60: 20 6e 6f 74 20 6f 70 65 6e 20 2f 70 72 6f 63 2f   not open /proc/
8d70: 73 74 61 74 0a 6f 70 65 6e 20 74 68 65 20 72 65  stat.open the re
8d80: 70 6f 73 69 74 6f 72 79 20 66 6f 72 20 77 72 69  pository for wri
8d90: 74 69 6e 67 3a 20 72 65 66 75 73 65 64 20 28 46  ting: refused (F
8da0: 69 6c 65 3a 3a 45 72 72 6f 72 3a 20 45 72 72 6f  ile::Error: Erro
8db0: 72 20 6f 70 65 6e 69 6e 67 20 66 69 6c 65 20 77  r opening file w
8dc0: 69 74 68 20 6d 6f 64 65 20 27 72 2b 27 3a 20 27  ith mode 'r+': '
8dd0: 2f 72 65 70 6f 73 69 74 6f 72 69 65 73 2f 64 65  /repositories/de
8de0: 6d 6f 2e 66 6f 73 73 69 6c 27 3a 20 52 65 61 64  mo.fossil': Read
8df0: 2d 6f 6e 6c 79 20 66 69 6c 65 20 73 79 73 74 65  -only file syste
8e00: 6d 29 0a 63 72 65 61 74 65 20 61 20 66 69 6c 65  m).create a file
8e10: 20 62 65 73 69 64 65 20 69 74 3a 20 72 65 66 75   beside it: refu
8e20: 73 65 64 20 28 46 69 6c 65 3a 3a 45 72 72 6f 72  sed (File::Error
8e30: 3a 20 45 72 72 6f 72 20 6f 70 65 6e 69 6e 67 20  : Error opening 
8e40: 66 69 6c 65 20 77 69 74 68 20 6d 6f 64 65 20 27  file with mode '
8e50: 77 27 3a 20 27 2f 72 65 70 6f 73 69 74 6f 72 69  w': '/repositori
8e60: 65 73 2f 78 27 3a 20 52 65 61 64 2d 6f 6e 6c 79  es/x': Read-only
8e70: 20 66 69 6c 65 20 73 79 73 74 65 6d 29 0a 72 65   file system).re
8e80: 61 64 20 74 68 65 20 72 65 70 6f 73 69 74 6f 72  ad the repositor
8e90: 79 3a 20 72 65 61 64 20 6f 6b 20 28 61 6c 6c 6f  y: read ok (allo
8ea0: 77 65 64 3a 20 72 65 61 64 2d 6f 6e 6c 79 29 0a  wed: read-only).
8eb0: 72 65 61 63 68 20 46 6f 73 73 69 6c 27 73 20 70  reach Fossil's p
8ec0: 6f 72 74 3a 20 72 65 66 75 73 65 64 20 28 53 6f  ort: refused (So
8ed0: 63 6b 65 74 3a 3a 43 6f 6e 6e 65 63 74 45 72 72  cket::ConnectErr
8ee0: 6f 72 3a 20 45 72 72 6f 72 20 63 6f 6e 6e 65 63  or: Error connec
8ef0: 74 69 6e 67 20 74 6f 20 27 31 32 37 2e 30 2e 30  ting to '127.0.0
8f00: 2e 31 3a 35 37 31 32 31 27 3a 20 43 6f 6e 6e 65  .1:57121': Conne
8f10: 63 74 69 6f 6e 20 72 65 66 75 73 65 64 29 0a 77  ction refused).w
8f20: 72 69 74 65 20 69 6e 20 69 74 73 20 73 6f 63 6b  rite in its sock
8f30: 65 74 20 64 69 72 65 63 74 6f 72 79 3a 20 77 72  et directory: wr
8f40: 69 74 74 65 6e 20 28 61 6c 6c 6f 77 65 64 3a 20  itten (allowed: 
8f50: 69 74 73 20 6f 6e 65 20 77 72 69 74 61 62 6c 65  its one writable
8f60: 20 70 6c 61 63 65 29 0a 0a 3d 3d 20 74 68 65 20   place)..== the 
8f70: 64 61 65 6d 6f 6e 27 73 20 6f 77 6e 20 76 69 65  daemon's own vie
8f80: 77 3a 20 61 20 70 72 6f 62 65 20 69 6e 20 70 6c  w: a probe in pl
8f90: 61 63 65 20 6f 66 20 69 74 73 20 70 72 6f 67 72  ace of its progr
8fa0: 61 6d 2c 20 73 74 61 72 74 65 64 20 74 68 72 6f  am, started thro
8fb0: 75 67 68 20 74 68 65 20 73 61 6d 65 20 64 61 65  ugh the same dae
8fc0: 6d 6f 6e 20 70 6f 6c 69 63 79 0a 64 61 65 6d 6f  mon policy.daemo
8fd0: 6e 20 70 72 6f 62 65 3a 20 75 69 64 3d 30 20 63  n probe: uid=0 c
8fe0: 61 70 73 3d 43 61 70 45 66 66 3a 20 30 30 30 30  aps=CapEff: 0000
8ff0: 30 31 66 66 66 66 66 66 66 66 66 66 0a 61 62 73  01ffffffffff.abs
9000: 65 6e 74 20 2f 74 6d 70 2f 73 41 2d 33 38 30 31  ent /tmp/sA-3801
9010: 37 33 33 2f 66 6f 73 73 69 6c 0a 61 62 73 65 6e  733/fossil.absen
9020: 74 20 2f 74 6d 70 2f 73 41 2d 33 38 30 31 37 33  t /tmp/sA-380173
9030: 33 2f 66 6f 73 73 69 6c 2f 64 65 6d 6f 2e 66 6f  3/fossil/demo.fo
9040: 73 73 69 6c 0a 70 72 6f 63 2d 70 61 74 68 73 2d  ssil.proc-paths-
9050: 72 65 61 64 61 62 6c 65 3d 30 20 28 6f 74 68 65  readable=0 (othe
9060: 72 20 70 72 6f 63 65 73 73 65 73 20 6f 66 20 74  r processes of t
9070: 68 65 20 73 61 6d 65 20 61 63 63 6f 75 6e 74 3b  he same account;
9080: 20 74 68 65 20 6c 69 76 65 20 64 61 65 6d 6f 6e   the live daemon
9090: 20 68 61 73 20 61 6e 20 61 63 63 6f 75 6e 74 20   has an account 
90a0: 6f 66 20 69 74 73 20 6f 77 6e 29 0a 61 67 65 6e  of its own).agen
90b0: 74 20 73 61 6e 64 62 6f 78 20 69 6e 73 69 64 65  t sandbox inside
90c0: 3a 20 73 74 61 72 74 65 64 0a 0a 3d 3d 20 72 65  : started..== re
90d0: 76 69 65 77 20 74 65 73 74 3a 20 74 68 65 20 2f  view test: the /
90e0: 72 65 63 65 69 70 74 20 61 6c 69 61 73 65 73 20  receipt aliases 
90f0: 72 65 61 63 68 20 74 68 65 20 72 65 63 65 69 70  reach the receip
9100: 74 20 73 65 72 76 69 63 65 2c 20 6e 65 76 65 72  t service, never
9110: 20 74 68 65 20 70 61 73 73 74 68 72 6f 75 67 68   the passthrough
9120: 0a 2f 72 65 63 65 69 70 74 20 2d 3e 20 73 65 72  ./receipt -> ser
9130: 76 69 63 65 20 62 6f 73 73 0a 2f 72 65 63 65 69  vice boss./recei
9140: 70 74 2f 20 2d 3e 20 73 65 72 76 69 63 65 20 62  pt/ -> service b
9150: 6f 73 73 0a 2f 72 65 63 65 69 70 74 2f 78 20 2d  oss./receipt/x -
9160: 3e 20 73 65 72 76 69 63 65 20 62 6f 73 73 0a 0a  > service boss..
9170: 3d 3d 20 72 65 76 69 65 77 20 74 65 73 74 3a 20  == review test: 
9180: 6f 6e 65 20 72 65 70 6f 73 69 74 6f 72 79 20 68  one repository h
9190: 65 6c 64 20 6c 6f 63 6b 65 64 20 77 68 69 6c 65  eld locked while
91a0: 20 61 6e 6f 74 68 65 72 20 69 73 20 61 73 6b 65   another is aske
91b0: 64 2c 20 73 74 72 61 69 67 68 74 20 61 74 20 74  d, straight at t
91c0: 68 65 20 73 65 72 76 69 63 65 27 73 20 73 6f 63  he service's soc
91d0: 6b 65 74 0a 64 65 6d 6f 20 28 6c 6f 63 6b 65 64  ket.demo (locked
91e0: 3d 54 72 75 65 29 3a 20 32 2e 30 32 20 73 20 20  =True): 2.02 s  
91f0: 7b 22 75 6e 61 76 61 69 6c 61 62 6c 65 22 3a 22  {"unavailable":"
9200: 73 71 6c 69 74 65 20 65 72 72 6f 72 20 35 3a 20  sqlite error 5: 
9210: 64 61 74 61 62 61 73 65 20 69 73 20 6c 6f 63 6b  database is lock
9220: 65 64 22 2c 22 72 65 74 72 79 5f 61 66 74 65 72  ed","retry_after
9230: 22 3a 31 7d 0a 64 65 6d 6f 32 20 28 6c 6f 63 6b  ":1}.demo2 (lock
9240: 65 64 3d 46 61 6c 73 65 29 3a 20 30 2e 30 20 73  ed=False): 0.0 s
9250: 20 20 7b 22 61 72 74 69 66 61 63 74 73 22 3a 5b    {"artifacts":[
9260: 7b 22 61 72 74 69 66 61 63 74 22 3a 22 62 62 31  {"artifact":"bb1
9270: 61 61 36 37 66 39 65 66 30 35 63 31 61 33 64 66  aa67f9ef05c1a3df
9280: 64 38 35 37 36 61 34 37 38 30 37 36 61 31 63 32  d8576a478076a1c2
9290: 62 61 65 31 31 36 34 33 63 31 36 62 37 63 34 33  bae11643c16b7c43
92a0: 65 38 33 35 36 62 31 33 64 31 61 63 0a 0a 3d 3d  e8356b13d1ac..==
92b0: 20 72 65 76 69 65 77 20 74 65 73 74 3a 20 69 6e   review test: in
92c0: 73 69 64 65 20 74 68 65 20 43 47 49 20 73 61 6e  side the CGI san
92d0: 64 62 6f 78 20 28 61 20 70 72 6f 62 65 20 69 6e  dbox (a probe in
92e0: 20 70 6c 61 63 65 20 6f 66 20 74 68 65 20 72 65   place of the re
92f0: 6c 65 61 73 65 27 73 20 70 72 6f 67 72 61 6d 29  lease's program)
9300: 0a 61 72 67 73 3a 20 2d 2d 72 65 63 65 69 70 74  .args: --receipt
9310: 2d 66 64 20 33 0a 43 61 70 45 66 66 3a 20 30 30  -fd 3.CapEff: 00
9320: 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0a 66  00000000000000.f
9330: 64 20 30 20 2d 3e 20 2f 64 65 76 2f 6e 75 6c 6c  d 0 -> /dev/null
9340: 0a 66 64 20 31 20 2d 3e 20 70 69 70 65 3a 5b 39  .fd 1 -> pipe:[9
9350: 38 37 35 36 38 31 39 34 5d 0a 66 64 20 31 30 20  87568194].fd 10 
9360: 2d 3e 20 2f 72 65 6c 65 61 73 65 2f 6f 63 78 32  -> /release/ocx2
9370: 2d 63 67 69 0a 66 64 20 32 20 2d 3e 20 70 69 70  -cgi.fd 2 -> pip
9380: 65 3a 5b 39 38 37 35 36 38 31 39 34 5d 0a 66 64  e:[987568194].fd
9390: 20 33 20 2d 3e 20 73 6f 63 6b 65 74 3a 5b 39 38   3 -> socket:[98
93a0: 37 35 36 37 34 33 34 5d 0a 66 64 20 34 20 2d 3e  7567434].fd 4 ->
93b0: 20 70 69 70 65 3a 5b 39 38 37 35 36 34 37 34 32   pipe:[987564742
93c0: 5d 0a 63 6f 6f 6b 69 65 3a 20 61 62 73 65 6e 74  ].cookie: absent
93d0: 20 6e 6f 6e 63 65 3a 20 61 62 73 65 6e 74 20 72   nonce: absent r
93e0: 65 70 6f 73 69 74 6f 72 79 3a 20 64 65 6d 6f 0a  epository: demo.
93f0: 61 62 73 65 6e 74 20 2f 74 6d 70 2f 73 41 2d 33  absent /tmp/sA-3
9400: 38 30 31 37 33 33 2f 66 6f 73 73 69 6c 0a 61 62  801733/fossil.ab
9410: 73 65 6e 74 20 2f 74 6d 70 2f 73 41 2d 33 38 30  sent /tmp/sA-380
9420: 31 37 33 33 2f 66 6f 73 73 69 6c 2f 64 65 6d 6f  1733/fossil/demo
9430: 2e 66 6f 73 73 69 6c 0a 6e 65 74 77 6f 72 6b 3a  .fossil.network:
9440: 20 30 30 30 0a 0a 3d 3d 20 72 65 76 69 65 77 20   000..== review 
9450: 74 65 73 74 3a 20 61 20 72 65 63 65 69 70 74 20  test: a receipt 
9460: 73 65 72 76 69 63 65 20 74 68 61 74 20 6c 69 73  service that lis
9470: 74 65 6e 73 20 62 75 74 20 6e 65 76 65 72 20 61  tens but never a
9480: 63 63 65 70 74 73 0a 73 74 61 74 75 73 20 35 30  ccepts.status 50
9490: 33 20 61 66 74 65 72 20 30 20 73 3a 20 7b 22 75  3 after 0 s: {"u
94a0: 6e 61 76 61 69 6c 61 62 6c 65 22 3a 22 72 65 63  navailable":"rec
94b0: 65 69 70 74 20 73 65 72 76 69 63 65 20 6e 6f 74  eipt service not
94c0: 20 61 6e 73 77 65 72 69 6e 67 22 7d 0a 0a 3d 3d   answering"}..==
94d0: 20 72 6f 6c 6c 62 61 63 6b 20 70 6f 69 6e 74 3a   rollback point:
94e0: 20 74 68 65 20 6c 61 75 6e 63 68 65 72 27 73 20   the launcher's 
94f0: 43 47 49 20 6c 69 6e 6b 20 69 73 20 6f 6e 65 20  CGI link is one 
9500: 66 69 6c 65 3b 20 74 68 65 20 70 72 65 76 69 6f  file; the previo
9510: 75 73 20 70 61 67 65 20 69 73 20 74 68 65 20 72  us page is the r
9520: 65 6c 65 61 73 65 27 73 20 6f 77 6e 20 6f 63 78  elease's own ocx
9530: 32 0a 6c 72 77 78 72 77 78 72 77 78 20 20 20 20  2.lrwxrwxrwx    
9540: 31 20 72 6f 6f 74 20 20 20 20 20 72 6f 6f 74 20  1 root     root 
9550: 20 20 20 20 20 20 20 20 20 20 31 34 32 20 4f 63            142 Oc
9560: 74 20 32 39 20 31 35 3a 34 31 20 2f 74 6d 70 2f  t 29 15:41 /tmp/
9570: 73 41 2d 33 38 30 31 37 33 33 2f 65 78 74 2f 6f  sA-3801733/ext/o
9580: 63 78 32 2d 63 67 69 20 2d 3e 20 2f 76 61 72 2f  cx2-cgi -> /var/
9590: 6c 69 62 2f 6f 63 78 2f 61 67 65 6e 74 73 2f 6f  lib/ocx/agents/o
95a0: 70 75 73 2d 6f 63 78 32 2f 70 72 6f 6a 65 63 74  pus-ocx2/project
95b0: 73 2f 6f 6c 6c 61 6d 61 2f 62 32 64 65 38 62 30  s/ollama/b2de8b0
95c0: 63 32 63 36 37 30 30 65 62 35 63 36 38 36 62 31  c2c6700eb5c686b1
95d0: 37 34 37 62 35 61 61 61 31 65 32 64 32 64 30 31  747b5aaa1e2d2d01
95e0: 33 37 30 61 63 35 35 66 32 65 35 61 31 35 31 61  370ac55f2e5a151a
95f0: 36 37 65 30 33 62 34 38 61 2f 77 6f 72 6b 2f 73  67e03b48a/work/s
9600: 74 61 67 65 41 2d 72 75 6e 31 30 2f 62 69 6e 2f  tageA-run10/bin/
9610: 73 74 61 72 74 2d 6f 63 78 0a 64 6f 6e 65 0a 00  start-ocx.done..
9620: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9630: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9640: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9650: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9660: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9670: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
96a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
96b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
96c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
96d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
96e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
96f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9710: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9720: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9730: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9740: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9750: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9760: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9770: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9790: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
97a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
97b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
97c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
97d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
97e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
97f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9800: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69  scripts-for-revi
9810: 65 77 2d 33 2f 73 68 61 72 64 2e 79 6d 6c 2e 64  ew-3/shard.yml.d
9820: 69 66 66 00 00 00 00 00 00 00 00 00 00 00 00 00  iff.............
9830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9860: 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30  ....0000644.0000
9870: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30  000.0000000.0000
9880: 30 30 30 31 32 33 30 00 31 35 32 37 30 36 36 34  0001230.15270664
9890: 36 31 33 00 30 31 36 32 32 37 00 20 30 00 00 00  613.016227. 0...
98a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
98b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
98c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
98d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
98e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
98f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9900: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00  .ustar  .root...
9910: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9920: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00  .........root...
9930: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9940: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
99a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
99b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
99c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
99d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
99e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
99f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9a00: 49 6e 64 65 78 3a 20 73 68 61 72 64 2e 79 6d 6c  Index: shard.yml
9a10: 0a 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d  .===============
9a20: 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d  ================
9a30: 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d  ================
9a40: 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d  ================
9a50: 3d 3d 3d 0a 2d 2d 2d 20 73 68 61 72 64 2e 79 6d  ===.--- shard.ym
9a60: 6c 0a 2b 2b 2b 20 73 68 61 72 64 2e 79 6d 6c 0a  l.+++ shard.yml.
9a70: 40 40 20 2d 39 2c 31 33 20 2b 39 2c 31 32 20 40  @@ -9,13 +9,12 @
9a80: 40 0a 20 64 65 70 65 6e 64 65 6e 63 69 65 73 3a  @. dependencies:
9a90: 0a 20 20 20 73 71 6c 69 74 65 33 3a 0a 20 20 20  .   sqlite3:.   
9aa0: 20 20 67 69 74 68 75 62 3a 20 63 72 79 73 74 61    github: crysta
9ab0: 6c 2d 6c 61 6e 67 2f 63 72 79 73 74 61 6c 2d 73  l-lang/crystal-s
9ac0: 71 6c 69 74 65 33 0a 20 20 20 20 20 76 65 72 73  qlite3.     vers
9ad0: 69 6f 6e 3a 20 30 2e 32 33 2e 30 0a 20 0a 2d 23  ion: 0.23.0. .-#
9ae0: 20 54 68 65 20 62 75 69 6c 64 20 73 74 65 70 3a   The build step:
9af0: 20 60 73 68 61 72 64 73 20 62 75 69 6c 64 60 20   `shards build` 
9b00: 70 75 74 73 20 74 68 65 73 65 20 69 6e 20 62 69  puts these in bi
9b10: 6e 2f 2e 20 53 69 6e 63 65 20 73 6c 69 63 65 20  n/. Since slice 
9b20: 31 20 4f 43 58 20 69 73 0a 2d 23 20 6f 6e 65 20  1 OCX is.-# one 
9b30: 70 72 6f 67 72 61 6d 2c 20 6f 63 78 32 20 28 64  program, ocx2 (d
9b40: 6f 63 73 2f 73 6c 69 63 65 73 2e 6d 64 29 2e 20  ocs/slices.md). 
9b50: 54 68 65 20 66 6f 75 72 20 62 75 69 6c 74 20 66  The four built f
9b60: 72 6f 6d 20 73 63 72 69 70 74 73 2f 20 61 72 65  rom scripts/ are
9b70: 20 74 68 65 20 6d 61 69 6e 74 61 69 6e 65 72 27   the maintainer'
9b80: 73 20 61 6e 64 20 72 75 6e 20 66 72 6f 6d 0a 2d  s and run from.-
9b90: 23 20 61 20 63 68 65 63 6b 6f 75 74 27 73 20 62  # a checkout's b
9ba0: 69 6e 2f 2e 0a 2b 23 20 73 68 61 72 64 73 20 62  in/..+# shards b
9bb0: 75 69 6c 64 20 70 75 74 73 20 4f 43 58 20 61 6e  uild puts OCX an
9bc0: 64 20 74 68 65 20 6d 61 69 6e 74 61 69 6e 65 72  d the maintainer
9bd0: 27 73 20 66 6f 75 72 20 68 6f 73 74 20 70 72 6f  's four host pro
9be0: 67 72 61 6d 73 20 69 6e 20 62 69 6e 2f 2e 0a 2b  grams in bin/..+
9bf0: 23 20 43 6f 70 79 20 73 65 72 76 65 72 73 2e 6a  # Copy servers.j
9c00: 73 6f 6e 20 62 65 73 69 64 65 20 74 68 65 20 62  son beside the b
9c10: 75 69 6c 74 20 70 72 6f 67 72 61 6d 73 20 77 68  uilt programs wh
9c20: 65 72 65 20 74 68 65 79 20 61 72 65 20 64 65 70  ere they are dep
9c30: 6c 6f 79 65 64 2e 0a 20 74 61 72 67 65 74 73 3a  loyed.. targets:
9c40: 0a 20 20 20 6f 63 78 32 3a 0a 20 20 20 20 20 6d  .   ocx2:.     m
9c50: 61 69 6e 3a 20 74 6f 6f 6c 73 2f 6f 63 78 32 2e  ain: tools/ocx2.
9c60: 63 72 0a 20 20 20 73 74 61 72 74 2d 66 6f 73 73  cr.   start-foss
9c70: 69 6c 3a 0a 20 20 20 20 20 6d 61 69 6e 3a 20 73  il:.     main: s
9c80: 63 72 69 70 74 73 2f 73 74 61 72 74 2d 66 6f 73  cripts/start-fos
9c90: 73 69 6c 2e 63 72 0a 0a 00 00 00 00 00 00 00 00  sil.cr..........
9ca0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9cb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9cc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9cd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9ce0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9cf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9d00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9d10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9d20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9d50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9d70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9d90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9dc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9dd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9de0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9df0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9e00: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69  scripts-for-revi
9e10: 65 77 2d 33 2f 52 45 41 44 4d 45 2e 74 78 74 00  ew-3/README.txt.
9e20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9e30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9e40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9e50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9e60: 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30  ....0000644.0000
9e70: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30  000.0000000.0000
9e80: 30 30 30 30 35 33 33 00 31 35 32 37 30 36 36 34  0000533.15270664
9e90: 36 31 33 00 30 31 35 31 37 37 00 20 30 00 00 00  613.015177. 0...
9ea0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9eb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9ec0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9ed0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9ee0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9ef0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9f00: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00  .ustar  .root...
9f10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9f20: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00  .........root...
9f30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9f40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9f50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9f60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9f70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9f90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9fa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
9ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a000: 54 68 69 72 64 20 72 65 76 69 65 77 20 63 6f 70  Third review cop
a010: 79 2c 20 32 30 32 36 2d 31 30 2d 32 39 20 7e 31  y, 2026-10-29 ~1
a020: 35 3a 34 35 20 55 54 43 3a 20 61 6c 6c 20 66 6f  5:45 UTC: all fo
a030: 75 72 20 73 63 72 69 70 74 73 2f 20 66 69 6c 65  ur scripts/ file
a040: 73 20 6d 61 64 65 20 70 6c 61 69 6e 2c 20 73 65  s made plain, se
a050: 74 74 69 6e 67 73 20 69 6e 20 73 65 72 76 65 72  ttings in server
a060: 73 2e 6a 73 6f 6e 20 62 65 73 69 64 65 20 65 61  s.json beside ea
a070: 63 68 20 70 72 6f 67 72 61 6d 2e 20 4e 6f 74 20  ch program. Not 
a080: 63 6f 6d 6d 69 74 74 65 64 2e 0a 52 65 61 64 69  committed..Readi
a090: 6e 67 20 6f 72 64 65 72 3a 20 73 65 72 76 65 72  ng order: server
a0a0: 73 2e 6a 73 6f 6e 20 28 65 76 65 72 79 20 73 65  s.json (every se
a0b0: 74 74 69 6e 67 20 70 65 72 20 73 65 72 76 65 72  tting per server
a0c0: 29 2c 20 74 68 65 6e 20 65 61 63 68 20 2e 63 72  ), then each .cr
a0d0: 20 66 72 6f 6d 20 69 74 73 20 74 6f 70 3a 20 77   from its top: w
a0e0: 68 61 74 20 69 74 20 64 6f 65 73 2c 20 74 68 65  hat it does, the
a0f0: 6e 20 69 74 73 20 73 74 65 70 73 2e 0a 72 65 63  n its steps..rec
a100: 6f 72 64 2e 74 78 74 3a 20 74 68 65 20 73 63 72  ord.txt: the scr
a110: 61 74 63 68 20 72 75 6e 20 61 67 61 69 6e 73 74  atch run against
a120: 20 74 68 65 73 65 20 66 69 6c 65 73 20 28 73 63   these files (sc
a130: 72 61 74 63 68 20 73 65 72 76 65 72 73 2e 6a 73  ratch servers.js
a140: 6f 6e 20 62 65 73 69 64 65 20 73 63 72 61 74 63  on beside scratc
a150: 68 20 63 6f 70 69 65 73 29 2e 0a 00 00 00 00 00  h copies).......
a160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a1a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a1b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a1c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a1d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a1e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a1f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a220: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a2a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a2b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a2c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a2d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a2e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a2f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a310: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a320: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a350: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a360: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a390: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a3a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a3b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a3c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a3d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a3e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a3f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a460: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a4a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a4b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a4c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a4d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a4e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a4f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a530: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a540: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a550: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a560: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a570: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a590: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a5a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a5b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a5c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a5d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a5e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
a5f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................