From 0a84c933feaee7415beb80acdf9912c39e9344e7 Mon Sep 17 00:00:00 2001 From: choldgraf Date: Tue, 21 Jan 2025 09:45:45 -0800 Subject: [PATCH 1/4] BLOG: Post about cross-project contribution --- .../blog/2025/jupyter-book-cors/featured.png | Bin 0 -> 37892 bytes content/blog/2025/jupyter-book-cors/index.md | 46 ++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 content/blog/2025/jupyter-book-cors/featured.png create mode 100644 content/blog/2025/jupyter-book-cors/index.md diff --git a/content/blog/2025/jupyter-book-cors/featured.png b/content/blog/2025/jupyter-book-cors/featured.png new file mode 100644 index 0000000000000000000000000000000000000000..11e3eafea8b9e47b6f717c148a1c3961fb56e490 GIT binary patch literal 37892 zcmeEubyQT*_wPj{98#qu1S~>IX%HDo34tL7WEe`N1sqa9Iwi*-BqSw0a3YKa#lU-c^K(d)b%3AE&JqG!-D| zeJs_X1sMdrdZVVSp#PNQ=XdW!xd3sBkz|l$MbO6S;=IY(^*}LNMO+smtD=)oCQrrujp5*fP$Pa9W#4GlS61|L6p7y(t=%Zl zJAtx8;k5rwRnG>n|2xB8Iwk$@j7#OT^S?8A1SKE@@$hFj+d=a0lFx!FtXPp$voH`-62UxdC-#6(2M>2y%p>4nAEVNcesrfe@~O zf3Jc);oL&^!|N@|;;)Hpz29KK&(Z?U_wgBFtkA65=b2y{GyD6Y^;FjXjXsC z%p_hEKe?E<9?e{@(we24%v6Mf%uU~k5J%7CBJyA1`;F^2ZIERCK4|^Uom-404=((d z6bQ4ZWhrDEK056&g6;A#puED0y}alY^uZq}VE^Dx4u%dKK6NAzEkmW3RBt*5 z1ffsfmc$MjJy<;IwS9&hl1lVwzSOq8G;zY3=n+Q9oAp~IH)tuwqzS)+t1hCY~w8d27B$H2v&;tviZcznui`^Iow!v1AZ}KB;g~a1Flo zUXzYgFDedpwh2C}Dnf8vriKuAqh2wW7oLJxqrs|jR=>V)nch&Uu9ByqyxhBwIakR@ z1tlp^@d!hZ^c~_LZBZ8~gqf*j&~>oMWk?!G>;YO-Cu9)3Ay<2Egu#ODJvrw*i)59Dl?Ie^{&9-FLt zA#;~)l@i*QxAH+NY<=OB%GQ8JrzXv>2D4^KEQS z<=F!0Dv`a6@j-2Q2l>BC+Jbxure1s`O3e&1@>`!pcYn{sdXw0*m(=ejd3@>H;!8}v zX&JG1qqBYU=wN+sO-XcSoeb*#2?XcSM7G+iIz;dAry(FY=etO_3rpaySEmxV(un*F0YBQg7aiB(94-1{bdX;`Io=l3`KI zJJS5((v4d1O6TaN`E|&Wh3p*uPBS+yh5+{sDi*P1ggf$LU8G+((amk2h5O)Mb{E{5 z+_iew{`l8)E6tgiuKXfoM*3()a(xd21XU>k-WeVudXY#rFcaETNf2oiS8_h|vEJ2r z8p&~Uk4YbopKjCW&4L}*jL~!d5gCt{usN{}61W@et{4lqQ~55-rhj0CoaYD zGBN?Y2QAp})!8C32wJIrzTA}QB9FN2TcPx7VX`Pg?XkZbEp(E~xguJx0=E!8IZsfG z&uYL6C^sP}pz66W=VFwWxIi=Kb~^tpE~nzUP4)Gx=0HO;pW%Z%5%n~C4guy>zkN?u zEH!0_VN;+~E_w(Ab4>7;|ijT;* zE7Kd#{7t?ct3-nL-TSkU=A@P=ViDLn{O#M)_erkuRW!9@r*p_<2r^Ot#2Epv8ZTsG zCu-d*#ue)dU!|~0`}5g)_qdjzQ#%)LEFknZrS7zIG1Cvjoea*W&2q>jGMeZvzS4nh zJ-I%6GPSuM=s%Oeiqd4C3k-_7(3WqTTMJ4WMyGXo{|=@diS@e)*KH{B_>CE&F)BHP zW1w9lZH&mU-o;~vj9ooR;@$DWW{&U|uCA_$RIuvJC#6OHt;l!18$ViTIuYg!6^tm7 zJ)HMID1r*tC8W>u{7k$fb{Y7eH!j=h|)a*4SX{*%%3=N=2MR_e3)!i(1E2 zEAoa}Wr)`VA~bB63T7F$Pp5wUw#c7nm2;ImaKPo}szk?~3KN{7aA(DM_LmR9sQLhn z3Um1O!4gnL6*Yd_hK2r`zM4k=&Z?NsEA_W+XU@-)(>ymBn1p8qzN@1^MUT4!mx&Ba z+pNc1Y2I|oB?Lj+Geu?gTQPk_?&$Z-gqO2X29xUd9Io=G@~jK^5mPExIkMbnm0EU)FfL;B9V9j|jd+3gDv?ZYuEXZ$dy=i;SO1RrIbq7F<%U=_1JGDfyAX^~ zQWb0YvO0|05Npz%8fHzfEe1r+D3vYPKf&}Qa$C|!+L`Mlw<1Fr7XJB%iHqS(OE$G97XVu0&iOf3UNv67YHjc7dsFR-dra@+K8z3F?_ zHgGP7RRG7EfgGqTAeaY=G*-SSid946@=a19Hiv*qdP~9%~Y2TLh{dUON^G<2R&DgMfVHl-k%)F$rvp-ZRm>x1%JDFNxuhCGPP1|M#WH5^uP)`LRcHuRe`1~Mx?Upo z=#`e~;*~o?mZw0R@x{BocR-CEf^VfO(!vLmWj%@qGpKRcEZt(yoyTYgOgV7JjLwTa z(yJN>=dX5KA&}W6ASv~oHZKpHcMY`7dTZDbMo27q13YwyH3T?YkInvVK=jtK54R(i zN2vJdN8HsA4Ddbem7kp3_Q366cbk7wiZG& zfe#}V8ccdnL_P&!-54i(3SE-wN`vwSO`k_K@3dr)BX3}hkbMdvD z{S0>dF7dFn;Yt)chECvBw%s*kBH5Wn{hrr^sbDVqvFD8-9i7l&W->D5KPp1c-fwtt+iklT!E+!y)mFyNv*GO`X+jKl7K zDP>lQl*2U( z1h>#zj!YWnF+$C93TRrW{NfEC+xoaC7_+Nq?I~Dblv8@mhBC9OBs=87Jbn3`goA}+LQZtfRU6g3 zF-{(q$j!en7$bG=FLtHdysD9GIjhy{WACp$B8BLgKy0Z0L*}&}RU!Wzhoso;Q7bCwC)$0;YcA|&_87xG*m$WS^uF{^e2z&7 zy(mW)c)ENB|S7-u{!R$A?qeFXfkDCu0)E{4P$QO?~&W8 zAXyS-KMj}Kr&zv0=>+M@`V0ip&LJ$5Bk`wpL^CU<`NN6M5Yf4rXcwc1;4%gg$&$E9 z2D{Ql2(ks%%U|Xt<(ySdl4W;-u;lD3e%^A*sz_2vGK^<_%y}TtEw8Q^3j|eMZ*$1M z7v%V`e`j~VW)NNP<(9rujqY1(rV@@4|20hKKiq}GNjF!|i_HeE)N|8ee)FKpg?J%I z?Li(Xbb1Q7EaY+>M7J&0W&V*jx8LE5W=W3hRTGcgvWjaV#M!p^;INzD^%)<-RM6UJ z8c|B_m0`?uSc*bLeqipEihS+3Fom%Pc%d8(;OO8DmzXO5>QAoA-z|3-K3B-EOy+yE z0b_XO5jaC-`1LJCahX)zJIb|#7<_R92wi+a@G1%flOID(wu3U9% z3^bTp`sHEbnYVE-V4Ubb68+@rbV$#k!MC8~cpRCS=aATT#XMVM5VwP>_b@r+ca=|k zMO3^i9S_wxV}(OpILqScNNeh}t&qG-JIYzRX6T=6OA@~`Qm=i#o-O-l_SIgP`@K90 zgg()NB)MN+bI0`_$;ro& zOoBHV#?tv*>XxG~A$ZS=VX0(N@o&@Z+xgI)#6w&}qE4&OUX=VdyT~r@T z!BORM_@Y4l&T96xjqO2giW<|biXz|KoYH0KDZ>v}HJiqpL#XQ8m6-#p4>w1=z~puo zR}&AitZ=MbN;0VsV`HFJLcU(^xqcE=4nXH|g!wyCMC$72hh*$YuW-|1n)p1hF<_F@ z2Cifr^Vp$}0b;zf`g0c80P-Z|WxFb+e*c0mV+MVkCi*`z7gFCEsukbh@e(NS9lx4@ zR%Em9wZ-9ul0McfD2(1uPTjrE*h@pbxV@hlTV8ca6;(h256#~^$xL8o+B}(QL zZ$#-GoQ}B5ctPg(b$&k?8+r^bi6Q)v`9754oG{e{$B8^%V%C>+0p z+`O7WsV&=*2icjl7be8CC#)LSQC1NwXFo9jxf9K5Ihd=xN7#E8L;}|Zc}^^!@Ac)) zx$b)PDl1&zW$g89_&fRMvaDy86)TF=`7=K|fky#Qyq+VbAl>sNchh`z+ZzlGJ`Z%9;Tx4U3<`1EoTorLg=hl&Q4ybdmj? z<_=kJM6K6K-jYJhKx>Jma5fXoxaL>{u7Z+WhQS!*b|*5Ym4x?S;A4ag#*F)+&XPqK z8&^pF6u%-DPYtKST3MvQR65e#^J_&rhewKs&UHL*`k+ciR)K%7*^D-$SI2F#Cng@I zYxU%$I((&dOF7APk&~k`*1hb_($M?<53qC#R|00D&eQZdZuC^dXas9;vlP>Lq1(vr z)uSJOOxf(eoaXpS|E~3>O|tobL!|A%E;TBesl2)9T!^QFallrUZ&I^#k+;WOoZ%>l zOqUDmk*gyhysqRP+fEt-+0;d&}m`<2m>1*ioroz%&S!*+-{s97?9wS zcocapixy;0liNl>b{r4l5wsrfNToPhB+k;}RJI7!Oo?%2aX%v(fBO4d*i0tvXG&Ig za*pvAJRsm5GS}*}#DBVET-Q}KChXo|nnHja(xh6vo%+2$rHd}yKa0p%4~;i@0~}T6 z-h5JJo(z(7CIS#Fmep_@8Kh`sCP9%U!6e8U?b+X$KbpB7oltkir#4kQ- z-z zE00|oz3y>FqSbO}=}myXD3Nqq)(_N%C_vtE>a}|XL$j?Su9so{4Y{DrLT8#Uj^nx^ z<;-R-NW4y{#GOUO+QjmYKOlFRI87MnTjC9IR-wNJ*JaTc21@s{LZe}Z&RMclb2iJW zARJu~WzB8#3BJU^3WGfMoQT0zbDF9_w~;w5tt8&?!Ojal0y3-Xi!TX#aX|eX&%_HF z?}oV@^_Scl{EHY)S-fmdPRe_ii{j%MWYZFKE!Ms{j>3s2?b+s0-(QYv-#PK4HGrmm zGH^Ecoh(BAzCpjs(tYat7X~Q1dn+46%nzm&9<6!rG%H+~oX_>Q3rIvp-*o$qJTs=(~JFCyKuuQ#r z_-E_P3{05<6lN|z?js#BV)wpy*f2sH_X*`@tH*mbTV1Qj7e(KQD#&lR=;~#2b`06g zM^AN*mh6nQj{=1~17@{~tDNKSf(^ZXr}$gk^lowO;B>$J6HrL;8t(6)^^?~;aGX2D zY9+K^^!{9Mr&~!Sb@z?im!ou{(=PBW;L7={9%9yO9!W>(LAhmE5S{;`T{H8XV_eTV zGCMXEjvCCYD|TNiZ+REM#LFbOEH_(cFAxKKf_J2ya|R3w#_aYX9H{ex9kU3sJ1VA= z_sS(wR?bxP)Jd2jYSt~@8Y_@Kx}r3U!Hh(+dHr$-#z<%S+zN3`(l2q_*p~b{Iq5`< zg?G0=cG}28zU0NBy8%r=zpDY%69!xf^P}=1ovb^S?q07sKMI=E3{g#1I8Cnk6sL-> zz78>3QQrG9T!Vqlpo!*R%V7>8u~1Y(@R#d5)*ifHou zMOcl5nP_k--lo>fx$|m338(7=!Erq|bYCbV)7`Q{<`HW0P}foBp@sd*0HnK-h)Ci? zde;71AVY;nkk3;XHZdAg+Zeg1b2X?pxCX^LV#$&_c9+HQnUa;?q*sX) zOi2+8yBVm3E-K%RSFGW(&k-Now?gp3%vizg@x|GZn%Yu|hk{arnA(?>b!BM549Q02buW)riLzOpGtYTO_|7@_3yV0!=Ul;ljJs=@-JD9QD2+9H}}lbP)NY#xnAZp$(t zZ%g}6gD7iC=Xpb*)T*o4&F}(-=67f9&bBqh?Vg{Z@mTb^e4i_h!Z+Dc4u(BU=Uo&D zvnq%=@8{6Kg~L-(*cu}HrJtBI-uYVLkZeg}-)x!8KRay``6ns*!S(fi8`rrMLd{@9 z4MF-^B54FDk=QuHrgOB@)=XG;=8LE7zGSLUq&&YT`dR%_l)Y;*DJ#z!B6MQ1RkrGL zQhL*yQ_>+S3SrWZxJB%O(*sgaJQTE-?pHdRr2jaLC!}e-A~1apItHlYy z=}E6IF!YKC5~L0ADZ6GQvupwE{n-o+OVr6kuf$ewDrZVlRty)a$3+OIj})OT&UX;+ zB{4mYS+wW2`~u?nH*m%~FtfF)8Ct4K*8_Ae+Zcxy$!wE37}9KR4PxWsheZO=ckhOA z%lSCf*1(0&WO_o-f6YbHu`b2Z%^7?j`Qvf;{C%eft5tZ=Q|Zp_Nkp`nmk zmC{Al#!1s~e58x;rN_vY%HuGAJ{p%ac*Z>yog2;A!E;JF{rMtw5^cr6#jjJb2_rG; zmr=Ucb%2!SJ{O@S#k(q}mGl24gUu=jq!grQei@W6gRj;okkjz`N8S{;eEq`!KdbB5 z8B(r+;bX_o@EDl1)KCXbI`xsENV}K;ml@@(|Fv{ct1_z|b)VF#P5cir^@bP2Pv#7Y z(MtkRgFaWHTHr;OXNuH@8vpJYToEc5O{zA*qx)C?fEs}}r(sF4e7#{D#_mQhjLbRO z(Rg^B?k1&D`QJROhN0hxR^;Wi&hzOnH*wWsLDGJ#*N2_qzQA!J^VsT<^rmoxWen*0 zh9ZWa8YM~K&LBs1FaaX8!{x3~BJ3S1b8!TTZgpA9EV3dQ{lx5@KY`S$9bKcCIaax^ z+>15dFI!g)T`|T6_<0^|_NF_bTjB|&7VEFXB zS<2x&^H>Yr3`k2o`l#^0z=Xk;MmlX&V1}GYFX!HkpIIV8svY_86@sx7p6kPXpReD( z*+yqBSApWB{Kih%&|0LwlxN`U-v=GAg@OClF?P~ioPd6(Hp3*c#oh96f$eO(3>RvV z(xDQvV8>-FXMRX;;o#S?WDjT$2&^a~w>|i5w_}mTKRYDkJrgZS-q28ys%Q=6shBYO zGOMvRrI-mOSmjdtPZ3A`a-mc#Ok$Qh{JbBHoZ*T+u0*oz@>=d=t4k?YtOj4{IgvjoKffY>Jw)(dvwzGWm=-H)EGI zAsxO%y%7~w`*SOJhw&;*dFiPOVBlGFl6~tOU-Vu!b=9Kyn(+XB!F+>^;ah?jVHnmoGcS3Os&Q zHEqy2mtWIelBg%*(Jff{6nMGqDt_m2HG_R$`lv)dHsu-&3zTr=9#nyV{^N+>Dt@GH zour@TNZ(-9<60}{Z#dQU{-;EU374V%nOw;)$&NLhFSA8mN4YzUk@wG8q~NakrxVk` z0EF=&kG!uc)3tgab0bJHgh8f?uyEAQ8zZ-SzeHvg*eltoEh&b;(kxH_C25UA(O zh7-J1y`BW&euxcg$^Q1Fn$efeD}56bs)ov`tm&!_ar8OU>^#f!OHgA?% zaE*YRn;RH*c9`uBwz4b#4i%dX-^OPcSrDw|1MBDfmxy&L422B@XNS$aEQ}vqNr?bB zlC1C0qxe(T5(E=T`TV=>RnXF_QdO{-`Oj}&{Vt`34tU4&2siU#S8y}UI@ot^TY=?E zc4HED^|dHq)-4LxB6IR2UcL>(MRX5dK|!s#Q}oFL8@nd0j^0qWd+-?sTeFlt&KPCelY;jqI8vyHxm#fWuezP(e-!K~~llAB)# zo6n>!3RfSq2HCdL80cnqNxYuaLd~!~PYvVljh`f{Am@x#@Y2)7S?W@?;P{1{NZI%? z7usxV>Dx#;YC(2XynknNfVR+Ft0_^4)inKymWBXtii=#S(s^a1c%Dd1It$!%F;U^* zeQ~Ybp`{Duod0b3b^?q4J%o75sPHSw&<)r7v2pRB!-Nhxf?orMz7@m_EkEJ8A#^ob&P!Z@`I+Se*rcfNPR(J7ewR^ zE8j|+Gg*nDmCP~=X#|`kX*bq{^RCVD{e#X?Gx8}WMrZh)s*Cp!#k}^de<)|5DRYzy z@J^E%15SyX@E3*TA{@;{az94*)4+9X<;@Nb9=1hcMw^#8vlguj&e8eH0yNZ3xOKVM zs?c*0$Ih2mL-@r#08x?wQFtS`>Mr z^N|A>`liF33utZCf#g!cH$L@9T;Az45n710!tuLIiDlWv)I$=8A-OTFKeR#jG@9ls8R~4#_AN8s4a4 z6Y_Kkw}i#tAFnD(f209RCD|s=abrP;Vto7cvzdmSg$N^;qev0Tbn=H!(pOUbj{cA{ zsQE2uMFyQIuwjz9jo6Fo_AytK=DH2`r={1-09&F4iZe4@5H~k%(0;a!TT}1`-R6Ep zqa&OiCAs#ios-V>`ytpKy;ux^T#d|{6FDUfKOhhj#LQsJYyh2p+E$J%!6K#k>d5(*n~#9@2ykb0 zB{(e&ybH$I`#8TgI|Ts1Qv7TE=!Ffb(H@Vu%nY7L!X(1O@9i!)?>*X9M6V6VxWIK$?P(U6Me|pZOY@mb`ecqRp)|5KmjGs{!A`jG@B?!fnF<)c zaL;2!kYDq@14mCy1YA1A*%?@H6ngY-<1(&5QCJdiXLpCB{ZQAzaD`{_aojQabD&uV z$vQ3xvP|6E?(eV%GE_SHbKIV~X9m>#x8kopvHZ~-LeB8Q)bIe*Xfy5Pl0<0pK3VeS zJ~-vJTZOA5jR=oNe&imBd;>PbAAwna#mAA`w!OtLUoxwW{wfy|D+bl zQ*b2{L3ay1q7RYou4wlJLx-?wJ*iw0Iy;N+Al}_`g8h(@k@*Qcx-B7i*^;S`Mj>2l z7^5UyH32E-a%pN@`6Q@{p!x|gbJRBOfR(3F#~)Qx#y-ChtuNDrBD9NgP4;}oGIf)O z`*nHm)-w8AW&Myytu%Xtp|0K6F_kBw7;?r5WmGW<+&2fwtQ+ZX0ROn<4t|zRyveB2 z-~9VowD0NFDJUt3mUrf!CV*E4x}n{3CF>cXqyYZNTJ@4t41d%xPUdMFBl$-fw>`Am zFp?<&H;bl@0_837S$}$T^iKnpTuH8Gsq|`U={?rSEKtRLhgg45LdFC|^s2Dr;sBWS zqad0ClQ29LA4Ax}WQN+1HpWv2f_V)hnA2Kw{PBE+?_jbfIJ zI*;EceWJaZgW)Mzu2Ron?=J*8#J}8e9AGj9ivi9BY#hyd=M86Qtv3qpcu5?7hSF_z zi;W#?{aB7a1)-2elC-KZk(~uH4XB1^)g(RPV8xK@t`&$cm_{;HB2%b?Agq&6PVv5& zG&V>JQb`w=5Z4s%MI|>V=*@pJy$0|>LZP?vf>iD8^~(LN04(Hmx`>ct!7|aqB}0(F z(S_0aG3K;z(`xGJY_Lm-X+3f-4)BmoRZ#p6Nzxe*Ery|xGY0sj$9>mJ>K;IdUN>GI zyD+3>IISuPSND*6qelzOS|O!8ZQqj3dBP*{4o&twq%$r)QmN@}1dG2_6RR_v#!C-# zwhdE30l$i~8VsB%x|?QRy!f%=*o6Ex)X^{@Y3j_{w)E&zKUIS^3x$|}^v~0u=)#CiThb+lncY4bk-2(midxx+AAiuvM-jUpl=HwUK*~nzPDoUmqJl!wX94( zaXTaxtDNo|M$%JxnT1jG^$xNpq3mZ9ULB|PDK2^y;Xp>$9aPQq)Pg9AA z`Ah_*B#^Q4yi*rky7+Rx5@A6)`L(L;I=C9rh$xtJdSF#n?asAKHwK7Hz{#XqH;<5k zpSz@AgO34FV}Mj2H2~ZOmDu{O2hCDk-mdHnJoof4!8LLInE+A-3WB(<8VoeOK_}Hx z{(hhs#^uyp=eSfOV{~>Wn9SdGQA)L431c&Dm%=a0MF$d`T~#*7R9O$lpJbXO=3JVW z^olGeHfs%Ldm6 zbe8~sD$PJL`7j20nJGTMd%I!W%k2KDkEs@;n|?IZ!It`DvF>js>5=9SI&nn zk79G`Fxw0@v9_`>zrn4#G( zYizgI>tpzW$C%r6Xeby0;z?0;Pv5Jw`cp7m2w>mf#^c(IWF3zV|4g7x+FAfOTgwEQ z&g`Kn!xagP$;@ZaCILX<3M(D9a@BdRQ9A%;^rSGP8Nk+)Kq`H@kc~&?VGG-7;$7^j z8v6qGOFw1~u(}$WxW$pED21>mnT{*a-a`T?GvvR_fU|v`E=79rehHfj2-b3PmZj=% zD7We(iI3+AuhGkeU}SbrMFhqp#l(v^n8V1WfgcO5!5x+bBJ_Ub;1^vsUUz{2GCU*4n4laNPfov zGA!9?XB!!eX&vLIR_WxECwpS}r2!mW^Uf>eOx=^dH=~}9J@1c{zI6pHAzA+m+!`i5od-w0~hf8pRXDgI(7+w8FvZBN zz|R1E4-VDXW+{*yP#NRj3cwzjbBIw7_?HBrT+8i44;0a=y*T{@(}^SH0dYqEw#c#+`ay#iZl|4jUt6d75bTYj6)n(1AxcF4LyoN%*3uPekX-<`#x7qIg8Sf7pJv!zgpJzjlb_6t@Onm> z4U*;|83ssDFi>DQA@w&=+kxzkc6*uI*$#}vp_{`+Rl?j)wq{38Udx`1u}VCUJCRJS zFBv9`#PL2{*CGiCm4%Fk9N{F3pYIOAq?=E~9+WUp!x-b&4{!U##xQ4T*=r_`J{B+R zZ|ni3$D8eiWW<%5*MQC!2x3S8&L%*RRxmhQ$_))T)B(?$f8ws2o1kf3;=_?Z+a=)} zTnhlZ#B3WF(<}9Ovf(@NF$m8mpnD^je3|b`l8l?3okBt&9#p}M7yzEK^r}YueFNJ+ z@2XuE2=#9jt3e{~0ecFsDT8i%Hi74`yOQKHKqg8_l%=! z=AXVC%3-}0b)GDXm;99UtZ53f0aH6sJNkE(3t(}Rf&Du#Y3HW(f3zBT+&TlB(f`F9 zf2v;~G?Eg)v8z1*xYd~9)iuq{u7Z=rQBbj!UghZeGiZ?2p&LFIhQWxU5hdKp&1C+B2ZA-pwe3$q@ zvGU$&=Z>O0;cU$7qRxn z`YncFJA3JMqzgLXQQUZ)=T=Gx#%2C9chq$dMbEzMl~I-}PzX6OD8X4Xc|heny7oG< zEC#I`#KF*Jiz}pp5li@1yda-At2-o0i!m)H&Y%9~PsaGy_YZqQsxr_+6-YzI$8!uI zv)C`D5-(B=3Y=IC47m)Ewos-T|Le$eFH@gwb~1&mz}G-&(2Xg*`eJs#Pq1Pk6(5qT zCuY$9DMOnj_v=U$ckWy)h~$97U9>e{Bgpa%s9f75<|^a(4BDcw>K+32GD65%)0i;^G7y- zqk>}`gz23C5lIThB(28VkD&C`f-wxSVw~WOd%?R4z(O!7CrpJZ4-uV~B*BW+$xaQ` zYde|bMmh-!m!g8M1Kx0qB8i0IG6q0S5XVfBnd9%J$_!^BydQrBO_AQgm?r?TTz!7$ z9=Wh9abhgH+m&kCIR^h>0B4lVy0{{@`}JW!Nr4EsS%|((0nkuZhg6yndtzH20eXXY zswd71(EDM1dV72@Mip2tXCAxm<-{8gPmBXrAjJ8)L1C%1%Y5Q>ykhm0kr#u%a#7YM z8qI>RS8POT^J(j#4 zgPM}xfW++{`=!j(t7oJf5&O$}o2yO&q90s&3Z&L0~nFN*-JM!eGkP^ zb*H)+H0Y_U_Ohy4q!a|u^D+T;z!;s03&(1!1v~p1+YZ6X0ch{%@wgjH<)hAX%~JY6 zjiaB-Sj7G7gl6q?D*f-pH&@#Ro{5nc4BnXe=&5a zy?dUollSDZ-LKG7Racs&j&GkDMsS1CSUYZ5{4*{}0dWLFdojNxehc=2J0a}OSC=EN z2o{{INbqRiZtlF^;5>i$`DUr$F9T)|;9BM7<@XtG^_+-BvRKLw(4H4Dem0IQ7(*Ee zc#$E>Of>(cL&Y^jt4ti{(W^?+0d@!EyFgEF-@z?uV|e1NK1dR?eF^;bYaT0p zM2}8{7Ni+6Hdq#kPlEU;%-NFQP|NRrEJhfh}&1FCN){9S`6Gs-uvWZ$n5$OSK7U*Pv z&G-`3&WL|PH?>zYZ9~Bbs^@@E2gRS5c^gOA+*{B23&fV0tDwxQb{VKRk)e|TxVqy7 zH3VL7UH>H&uMBXYC!@gEcwZC!@8U_dGzz^dkKK!6Apg}aj_Dq1^T*9#*Og*M+~4_{x3cX5H{ZbbeUIdne}80uIv9H z5aiE*XG3t27A$Tb|JPjxO}{oopN4`^Y_bw!KSwh5@%Z1=9Wws}h&VN|k>TX{pYIHu zkm3K=?+pAOK_K?#pB(@5HHrTxf-U-wnp|?LCu)*$+r!WN?|LJ$rmlW{i*91{7$4u% zQe^r2#k<&+!nsn+1Zbo5H~+_vpCfx=llS}9)5;q>%-%Ohno%D52xhezjafV_T7CGc zXtk;n42rV{`;<6~rpB<7obYWMIbpe3pNk2&xzU)^$E#^?9g4!(ZH+3P{(9j3BV?*+ z1`f1-S+{WGxc{Z_gS{tBWXxZGek-Ga`w%-H!7%EpdES0LB|C?X(bcnL&)%iwYaF!a zE?i3dPqPqkG|!J7jLSVx9P)G7@7F``za6dcy--UEYy{c`Av;a!-P< z004DCNyaC^f(;DTT61yVYS|)1+roTrl&x<076c_-I5Fbz^Is3f>nrv~zEL)pJDN;V zIE`WZOe}!UcA1~3%l!kpby*fx{(fg2+v+x^f&8uhedvVYK2wVnCurVIHzM{$f^bd0 zFOtqmB5I58^~%Lt2yz;wya{b@kF$y2erj&-_ZqRmO4_fQWK(jT7 z^qS6gSTq(F3mupp+ow8=mUq^6EPlya1sUq{K`q%~Fff+WgiiRTAu5#9)`2tCM&dWU z{ROel2bB%0$@Tr(oF-q5VRXi^GG9=1(gXeX>EyPtz>5*+cVqne{pn1!g!k^_+NnS) zYKWdXfc@53rBkKfVf$Ep>>Q|n+UBs$p z_drr+28%2=HgF>=>qj5G%#5An8jTCXlb9-UfK0c9MP-5nt)_pAzQAURLFyx^0X(8JtDoezDp~1;yaCZ zCpqIz)lW$JJ+z6cobc+VB4iHiEnA_EYwp-CNb5)=5_gbLd4W|a^+p^aib~zS`|sji`I{#VucdA)fnkr2Mu|>e z$3EnAH83-5hfeq;Ym~YAF)R?mr(<73>ZFUVTN`i*!p_Nh5<)h8;UzH>{Qa0&sc(6VuHjL2CI#dr&h0 zpS$>J(j4k){Z{&Fe>M_1koxpN`Ez41k*a}N2TWR1Ia;@xNu`_nF6!Xd!svwAvEJ0W z&eM^-wd(}0uTn~zp>(nGpIi1`#6J53@*QV8;&kXe+5z%(z2RW($WQiXY0J-uZsEP# zHR&IV7V;U1U$SBTRQ=o2HxGO+#(8~`p^HYwEgrOhwV>_}(=o`(gpN)g_E_%59zXRS zMrc{26raxlH5~|AT0Btt?T*C1%*p>7?4n5KoQZt_7vcb4#Sk}ai=Meb>hDE4dpnzc zjWFgYYhx2|ygs=OzFW#Q9tXL}ZGG5rG;&Z<6xbOX5&!Bgw;4)vH0vVa zI&j*aBmU6Ww0r7@Dbh4iy#er&WgtU~1=-nZa;zyIRLn~3U|&-@-ZDFSw|rVB#5q=O zH}P+)*+|c)74ZGuetD(qG!!u+q9wv^& z&j``F1{U6+;PQk`nF4tG!ElMMrS`2~!IhT}XE=`&;X*-TWVN-f&RH*;M5EwB+4Zr) zlady7;4?1focNrE=zV`jS{`aH%$^U-* zI}d!xcFwhPu5-mF-tYHk>UeDm6-Z9}osHp2ogY?$jXWug?gtqnQk-(DFR}>oX_nTd z>7d8CzHhd8lo}$yMe{-?9`kL|+iR%aqC3MS1iq2>UA(CnQL~;1Dz2 zm+ulU0eC%>?R9eaL4}f1%9)2^dMpqSKNQLq zd`ROC>oRos>FMDyGE``=R@%yleib0RiXn|`adsU1v-$`mn;;owUU%xI9Ii5CH%Y>I zY-^pG6}6>L_8JMZ*S`>_G(6#H0*U{*YLM~ku^fuigd)@O6$>3}IN&5NK5MAXVE*ZO z?2_l{k7fQDEk6mCn^+NOM;OCwmL10cQV!_+bl04nA40KLn#Yl5ArRF^JZwt()+u-w zhvh5QyVITVPva1&b-O2%$NIr|%iR+Zpny}+%We5c0N53=-^@L472*eu0@VsU=U@@u zUmLvNhUMyNe>To8zCC=hz9442cSaGUi5{JHjwcK-uh#wkw0WqQ*!y!=)G zand(bHIpzJxZhO%p-<~uDZmk3UO5DIB=7<$Pw~2`7d9fMooPgPcfSb3+_F65n^b6Q zDtV|&*&asQt{$D3SCxAY9tI>#VJ-lE=$GeOAc7D)XCJxz!3KTjLw^Xs8Y&D~MTB@w z9?^6B`%z|rgUauPJjHun0T0_Be27<3usC$yRC*3{p03r%xy8Y!);i{_>K*_m`*6p|e!_0CXQ~q{_iTWXH%Sw;^PQIa6`TET z)BQ9)fcRzWPophCb9CH>fPFxF*{y6_@F810>!^XPso!$e(LlMi>he~3(w>HJsUKH$ zZ+MD#G_Y?044@OFhQyXeV#YFq4pd8EnC*0lQ1(PI59Q@O@0jrhlwqN>f{or3)tD4%C-+PncCasP)GzdFGS&X$(9)8T|{kYZn zCydQvlZ#d%=kL7D5ixK{-f_J8_ffs9%U0YJ+e8e=k&M0LXPwAgC zt6iPlk(~^S=aF6Vl26uReVWjzs<8Ev^}SKs^d6$-n~PmE8U2Wj+Ic8gXsW>mvm?I{ z)d~9&IGfg2;r*WrS^Z4O$X5w6ZWXypH|Ts;j5&uobwsniEDlJ zV#KRCM#dG@| zBiyuYsvh0!vH!_Q)iu9b;8r02Y&b9bOfc4Y#CswgNvNHykprEkQoiH$Z?(e|2G0iG zXdLQ6)iSB5UVN zdz1<-$CG0VWMBHpzs1Q}-Hp2i6=c$V?AqYSF?N6NIY56xRq{fHxnEf_D=r_yf$I}C zqBcw(+}%{ci&Zd?*Or{Q*SZl$0`Ikme;fa8b)S+;!!`!}K{6bs3`=ZVX0O+6$g2P_ z*orQRq7Ek%C>)~%zq!V3)KeJk*%vTXY;re0Xd|)d-siZW`E_6bC5hSvk%xn>-G1oi^_Yy2{ax}Y#5Qp_8Rx((4NT^*V10ioD&@{ zSPY@%+LT|Z3_RXWkMZ{l7(4D$IPu4oIONX&dVfU@AcL<2i`ix%rax2?JcrGV|N5;o z98CIe_ppBHUd(VRUoWZjNxXL2IL7nlp)~eSB48oNw&>O5oeqIy5G=OFmzQ zt$OFDjHQs4VeYj{8^STuxfY^M^?x64ls(?P)P?)qa2WrFTCk*cwccU3q{oPvjy1xZ zywn(3L$ybszs21szKucCMIypf=^V38y9LJSin~s63X$t!R2h#LsGag2cU9{l`Y&r(q7EDMDr-Uyd|-DFeiIRaFvEH76*WwT z2makyD=lHbB2VVE=^chooggBH&(7`2dzTpVvKbwnTQuPUuG}wr9rz$sCHJpF!Mv`u z2-lmJo-Wynz^nV|RdF*3XPVr8kjlaZz8GY9uGcK)c?Hcp6?}`n0eUA*)R(LjSiz3k z8a5Tm7oRb_ZB!vvoPwHX{#AHbk^(b6s%(5AP#ogCOLyN;FJDY>{X=jd{zA~cO38}Q z&p!QruOU4(T)j4iuJ)lJi%FE=Alyh@;96oU3N`_arcS4!XMIGc0jO0L+r+@+B8R3!TnQqOT)*90UO(`>ICe;N zNjf%7#-8>$oes;HTQ%G4Y5;rc>;q}KY|4hw8!LAe=a*G}W_Ep}HjUv#_S#Na%ouO3 zVlgW;?sg(Qda<3hLDy;jeV^~C>n&s31YugT*^rp_nRHpg)}swiX)9wED9rHo-W%?J z&1ThHM*_fju^u?08ry|c-8mOnnmp83@`(3ar5Wq8vtV>a9aodGcvNOWnvk#d^&mS_ zjIzu@tvZm|tmIt`$hUu8?PhUNDkvA^UTEcxy{wy6$_}jI`bv=}+t`=pi9h+VVW?~0 z*qh{rYzU~pmko4M*q;j@*OR#-U=FkGETpmzhjlGQbqTCAYH~W5&DYmY0&>|K`j$vo za)oJ`P+8qm{?G15Z=B^#wwq@GnfhqVS7zt+NoS?-fl2{s?!j%Zwg~IYF{`>DAM{>% z)+TE9gIm*SRek2SH=!S)G5FenqbW+f9N<^WUsl?5vzCkr3DemcwlBDybX@S__zTxh z{EDSAH+B3zgw&6-;KMT&J?guPx%<}tdMsMmc1#tSjAC|pDP8b?VrC zMARt|KCJypwAuF$#oV_>ev_~r39pkfoR4eMS}I{sKZLQz)h-;q<=5(CuhP*>mg z;-$)jp`iNqbj{o~OlLNJyFIALxpw(h6bdha?;9S@u2kWFGX3$)soUywE%X~tzo&{w z^WLl2ftgw4az3`aUAlt4+*Q**RfBdCSQu!etCnd+b`QL}Vb0veaJwN!bs|34As{zH zXjLrYLtm;RQtkbD4|C`bQvHrq0d~$GMnrBj1royFb2*!zNL=6CE2OEiB@*!`yR`!w zfdT^?y9~$h#@z<;x#;LDIso=#6%P#riC zfMsXxm^hJWdBzM+xv8^rg8fwQ8SgPG+slcsW?OyBeI5$LOT9AizdJc1)uIlK>5Z9x z3Iq>4BjfU5g3Tz&k3;!HyL-jALeJ!W=fvJ>U)eK;%_*RK*HB4lxZU_7sE1%SYu+Dx&yz# z1|MvfN82hl{r5rZ*Ib_EqcDrA3NBdRKxDRJhP!Z zP37h3bhH73>08)%hm-xCam=*@8oIO0>h>e224;1vqGhc3=&e&k z>G>tgffuo3QfQO1V%Vm{V;s`k!qfqkSo$Sp(GKtHh(H3?qe#FUA9ooAeKa%7oCBqM z%UmyDh%)xcD^;P26QNUQZXQBc4Awda(imN=O|y(Mes^rHln`|;odh(W%{>SXf2*F{ z^yNg5{mH90dTW=<3bB)Eg3~RAB9VEhl`_4XaBSz}%v7#~8|$Cb>f)B<3vi|}-ssh*RzS!psE^rfge-ovJn6og+c^95 zM4}DE9;fsUrRewkdye+CPnzg=0#x#ZbEHLXL4SxpqqDg`xclM$FBK}o_AT3RNsTclX=i>R5R6ZjHo0*>*s_~%5u`Z2 z(@$qh1eGY$6kv{vtofdA`3w^geZMVVbrbhAVn^W1RiIEB7H0^FGe!_Rao@#!wq7Jl zd!nkGVTHMdyr>q#Fnd?p(ZZ|^5Q6Mps~xc81c7+NxvIk2eJLg71~Dk|*d+pn!it@N zmY751UewOL^O8VdQAX$+^cvPCm}cSi=S*{ZlGop(A5DK$uCSg8Y%`;??*Ae+d5*#P z6Jj~LF^^ZOwK%Y1{YHO7%#?jk?^OKQ%i~HzvHY0BlT5a$1*C@7D~#mTfW9|V_cQbk zYg;g7crKSjm7AFC4k+iR48h9wiEW_Pk<(J`MmNvuZ8<{|e2PKZrxME@^x-=L4TIro z4zVyl?VA9Ak|24Y+0_UFlqDq`SrBcQ3CEHqm^0FVGhxxaOv47Za@{{AA6yFZFNVxm zWZ7jJV-odcs5Z{*OB1|d8e$!2bQ8R4BDUwH9)?GgDnH6|?@?bejF4|6a3x_~&8X!e zjpMa#<8_X=2K<}@J33*SSX2y|uJmQj+Wl3I@0GcB4F<>~m;n?k><1vg zRa~rcu}ln41uo35Px{4AjF62(%$}I*ZF=ubUiBhQo|K+=3%%#qS#GMx8uZJi4z?f6 z^I;1+^MsLIzrA;TqK@In&MX~Bh#XY~d_h#gDm)&KtYMgPpH!k&ZlHtGNq5(!y`KzJ znv}ncZqE(Yf_j0_@cYqnTR&*7>+vlz6}RbwO>D%|#?=fPmS!bE;b88BF&UP&A!+&o zRy|`Na1<=MBJB(wT}OKPMt3rdH=DSeKPHbGizFr5cZzFaFkq{he>Rlo zPaHQ{HZC$a+U4Z?owQl3$qY35ZE4FK4z?e241F&_IitdOrGt$HW~CE<`>nmXAiqSz z(4(o;29P>JPC?M;x2rv{Ya|oFeLBH+M$k1;`+|tq35cVu<85J$>5ow>50QRyc)jJC zEmBF4oKansph5X%|HHm8_ddBQM7Ic$O@a&Lp%`bRQ$G4a4fIO{kr}=UI#FabJutW( zi1TZ|KDGRSIds-==}yz;;X(r(8Kvf8Yloj)SO1`_)E_%KRV83Z7PtG^4sDtQyd(z+ zy_ScT4V}uw9&ra8Z&Dv&!<(=dNZw%Q>Nqgu-8*sObV#mZDUa0HP(uy<&c##JHhLY* zC-|*76;HJ$krmBw`gF!9WDr!(p3sRj-hr}Zvwfi(C_U^1o z+BeBKJuEo7>pUqNr6FAGn#b%Q{QZt$-XXHiS7Zn=igV|2vf-Hl;x8@`sPIkF0^IrR zYcK7VJ3!yC{T{;MMT3>@cb!Q)yT{^mS3c_s&CvsPsiFQJ-yIrtr7=NtA+#(PQux)b zr@DZW!->SA==>y#C3uj+yh4+QUJ+*QPCQ|*K_`DSc6W>A;%{<$?2QG9OcZB@xk+Cs zW;qSba#43My?w`3l|`*b+7-%c){nRy#wt?jmq=Fv0G9*s0kV0ui#2XD_cF*#cnIVp zFI7ovw{o;p78+$Qs-y(}R3#!geMOdB84lsPV)?}EyI`7fHhbn8*6_4mX))Bsp;rg4 zRtY%#z#)ur8~;{q6&!pFgdiF#$3tV^qv|>=Z<@Y>SDb;5L!L}o3iqQWI(3M&l2$w&;Tago(5()Q0d7SbS!tnJzy8|NZx8+dC#<)Z^oxLcs?=-M^EPBF_TV z=4Pr-=jUGk`&SxgKtin}py5!kA&M`reLfx3Q;GOgtAq7gpZSo$Eg%%5F)r3SJa9K_ zWf+BtFnUy6nJ+kp3QJ*+|NS1*Y*&QOoTjc>H=e2}I3|v1ePx*Sr<-SHUl$4Zb-!Vw z@ab)l1WahpNF{sjnA&&4act9M&Da9_KAu7vI;9* z!5l5z`=YTG_&`}Bng__ZC-Bx+GNG%g9Zb%|=Ize5Gz@#4c$KUE?bC6_Mmx{VimPN! z$2^_3PAk;^p(>U^W;dpKw;KXaKiz$g#!!#@4FF%6a4Hc*B%Q|XM3#JJ1cUVoSZvoS z3g8un{CX2C{3*C8IG?f<)=mxcOLZ1#MuRS8l<0`zw%jIKd$VKjXNgasFl_mfs@Mz= z4D2;Vn?nh=V!rp~Y2hxoFykrY%Xshi&T4Wt@iOgih?>g^Mey;|HYYl#TL6~x6EXDLp!5Q`oz zhB>1gjB;z9vxIjO+C2n@26z9itnrgKE_d;ozmaDZaT(B!SWl27{5kjhR8R~|arU93 zPCj`wIT^WCIhCMXlO{O4Qt{ktV~1MipeLcX61s8W1Md3KRQe=h@RomDID=EUtgCpT z`=36KOC}FcVIs(}&i4kRb6K_O{PDy!V#hVBKkgH01p&9ir0pxEB22wY*w=JkA}uA? zP==Um5&l5YQ*i}e)s8}9@$Apoc`nXH*ILxAJmF7$;qwJ&#F8MR^P~#KcY?rDNfLHo z4=j$#xEbW^JwfDzeZMw_yqjv0IT9pGx>C0uyC#8U?t)&JW%R-?D%e!{&kycm3ujmP z_`*$?VLIASHy)Dym&uEAMamMIIuBTg{s$x=Gt^FsjIH-{hduQCjR0?;_ z^>omw%q>@KQ}nipK+yV|FGA}Tt}kDBK)8~Bzz|t*s{d@#H}=Y>+QcqTIi?~{s6(e} z=Eb!`(R91TN)XGplxJJKwzbaSEO2ago7TH+wrF{X18R_0C zx5qjBUhPXX*F%5Yyws8Vw>;|B1^z=oB}9tcU}>?Nde4;ix5hW+4Nq*hBt@dEHskqp zYk?%lC#8tsU}oaKGRLlJtR>o|*4Cp~{FfVXh&?TUvu6zKv-z-HN(6#ELbC4%9nq8ub9c9i$LVy2ZkuQwVNIxDeNp577A0bVcw& zwyvIZ3WR-G-*0iVytj0Z`_-MHJ&=IQ90h^*MF7FbG#>e*@lSlpE6%B#Hb(Z+;So@z z^J?pgv^2=rt+Lz?4LGtDeHG`dv|Q_887Py$WE>)^A@t`-dE_0W{h2(&2MZhkqp1(w zc(0eDSUd?#SZi~P;<|?A;U^a8$3AUpMLa`Pf2AN@56DjMlr#l&wn!ftsExK|tG!YX zb$qzQ;krJQWNAFI$E_{b(c+8H*nirP<@#4%3%GhWOi77Gx*gk>0i@Pt2IyA)3IOq; z72!KYVIODvH^UIbcKiHFeF6gG1)WH%t}6VENR{edIdoIJd?@36HTEVlB4!8G-NLAQ zG0e@puhB8$WUc)OJt)z5XL?cLK*VJCEwVGO%<~OjNaq4v3j{72 zHmh_j%c|Ml<+fWD{gPj>l~+Xv7ur~^`UlUeIE z5O+*NQ6;xjg@nLCv)jEgeOv1V*&k4c;z46<#G1^j07Fo;zkv&vgV;ax*iHBQ4ojrO zG7Q!+4uMNy&&&sp0}8xwAJQ6j^L=%WaVHh)XPXGdCDO&?ll?wnYwoS zhxBLxD(eP}4A;zj5nZ_I0%fUmoqenUg4*>~_5*=I;}G4WvI^w&5H?=`9bisTE``QAhNnljC>h1jhxm=va_%AcnlWV5gMf+~#9&3fg%v z#4aQI0&8Y5W<>OaJj!%wYY)6?aHYIm^;J%W#5?=|1rVMD4{g8G^jbDsnel9fQP@Z3 z+pz{TW}k$UNYqknm?mtv%y~yM+#+tW3wI0)z3=2J%VKC$mp*pJC+#}@X+5+#*2b<9 z?X2tHYU_`$O(P@cD)px3ye;-b21X~HO1xM7oV<5-zliv)mCB_qM2;aZNQu7kOmSY} zk10)$NIZRn2UZp>1^ScvYUbU-C$vi_w)UINOxrtcR13gVKc^(tYrc4erL@dcLo;;b z4|2BGAUObTel}xrlw;UdmBX|w2XPl0a0^K3V|$;~z#?=M)OZcCow@Z$Xu7chdO*av zLJk#{=?fPDLjPk%Q62POEhJY5>k55uIue&=8SE-PZa2PgK3OC@hZe;VmzK+PR(*#* z_1|+WsPZyrMkp{abs>F*&EF>8pt)LG4NR1gYPz|C*lt$im`;@oaxZ{3r#JpnQF_8$ z>D#ApNBSmM|Gd{*1JC@=2LPmYpXV%46$ti4exVD57q>!7BJVs2Y&W?){fjR~R)3o) za!L$Z1*M;3mOds~o{*0FgnlR6e?)bxFx2%wH<+{k)>DxC{1L9iUKsllO-G!lXk4L0 zI==@$HfSHUwoFD6j=NLiJ^2>>ZyTZ!HFqOCLu-!-QiZXfZMx$}LB{=*`?Q+E;m4W` zvea;#-&-{nHxU8ZI*=Pjf@%b;3Dj0JdiKe(KHv7dnIedY@6}am)|?@n6g)2vB?6A9m4d>o4^BEjHyLs_&o=f4_^Sm{)z``5Rg*Bu!1JW6*&`eY(tVu_> zvFQ76*(Btc9nh`^seL4Ox}zfN`7|;7CJYaICsLWK{Fn#Kd!7tmA_~WT{^FOG z%({(60=w4qd!p{0 zj=zz?o?+QR2K=5`nM(7Qal~8{exJsmfIh@l{{3vK!uWOIZ_Sgh41o8cYAWfJQJOhU z>*sq%l=-}XZq(B$G>YF?TY>DMva6ZOezMV(FDASq?I?W}jnHHp^v`xH6aELfapvFJ z;o0w(&H#eDE=Ek{wP7&MeZq5Cyh6G+UvhCHaIdacFfVZHbNWz&<&)D^SeO_~h7lTJ zxS!M^7Ev*LtEeiPrS0-h7B#bIiC>e(GqmP@!@^ZLYd)LOQ5rZ`AV6+NKFly^a#Y0c z19Uc`J5uFjt1mUwwMX%LGqP%e6_Kue1V77|N!UWbSc~`yrV*<}H!r`HE)rF5lq>`ztZzD7IJWjl zw_Z#+b5CHkzZ14Gf0%??VJ@h)Xzj+qY+Q7phQ&gKl%gP(Cx^qt@Tb1LxBX$j>rxS# z^%toMJ6^~d=O?Guo^3tXe2G=%+C|fHVY>AyMLg>Dwo1`Pz=mJQsas(#riJf0)T@wy zaGALtqvO1*)bfx#euJRGqImlSUF$r|3%l>YWJJ*0tIAXeZlOUs&;(%9oX0E5tPUFS*{y7hFNYz3SgO5sA$d@B9;WNZZMn`+@Yc*S|fbhCZj! zZ|(FtpJ1(?yQ`z&tF-_#lLwTcvQcBcTV_2 zL$~GJgt2Sh$_Yg8SuMQL-#Wi^n46zpCbUqFkg(cdg zT5b`igtVCL#lDgwS^V$9gqiew-55{2 z@xGBi+6Wi2;|HD3(-pA`&g(CoL`GlUq5>kLl5Xn<5_6xQI}X1&K{RZvmgX#bb4edX zm4!NOa4DG1#BsO?p_UduDXrIQ-_{dZ6;%eMoY`KhsJgA~O+^L0hQ6 zb-?4=3(`8ur@IAoSbx;A6j`4>2MGxQsf7{meph;#73lwjd&T*}_(`a_M@mDKoa06o z@o4bNKSADrAWZ2~D)k1hjvRppg@(8V+6T1?FrB`p06N4!`&>#EyFfx?qK^mD?0UM; z@lL|d+Ew*TWJ3s#uk1hMhB40G&p`$qsTZS`n4zJdMvzx`K%G<*rHVENXV<5~w@OEY z80!IvbP|c$;q24b@G9>=G67f~D7jV{4HN|`zT5U<@xS9P)Kl`Kava zIH!PI7gqpPEAsqzmF11pL}OaM(a2=J@z6E+rCZ~tz9|Y9)p{InjkW=?VN>reQ`?c! zfn4!S5^>U}NoI)D$<^ztX}uf(Q#$=k^qpW$eJubAG{l=3Gponz$Q9$_E+PR`h= zjc2dI_V?~HC<$_?%9LVnmETgbhQ(ex9nb)CVY69mnc7tvjvCzNU3dQkz^bTNH1sAv zU9E>67VaaEe;eh??Pz#5pKyQE8~wDH*toHJa@b5Du(@oZUq7VrQ{+1M4j(QT^+6U< z>`K={anJqt_Z^pV&c#>oOF9F*4jM%J$%^YdLKO^#Hko3@qr>BRcApIisi^WHlpqdf zKIqaTK73=WvM~5pQ@VYD_;@B9MrAEEQG8>#AFaA4*~6;eB+b0c?)IG&f zI+g@9np*}1>}T$d=sidFTc*wiG=)p#T@!V>drh)tgAn6LT=!*1iD>WT;Djd5ahzk@fO{@rw zZZW#S;2c)^@kqaZqKVKK`tGKQ3RIT*5DewAz7*A4M!1z?J)E)?HeeG@ZhdDvhU`o) zJK0KCkn5>iBA$r-)im=NKUeA1rpDx;g_$dxYGm@wXax?*mDj)hq~36DsU2mYN)dBE z_V4`G4X-~>Wf0so#W}+R(x)=SW94DGS+A*0ALq4-m)VikK&Nk#Tm5B&bg{+lZ{2L( zG*|WVVi@NlEKjMRV9r0E#KVs~>YPA6)D~xk(jV*_^l}LGb=W$w}_71FG?=R3>_oetdB%D7?(Fi=8rp_Co3i<8sg)F0)5e8;;tTKI?Gi*(P57s z>(&B+UwlxL<-dGi2;S#&kRM5aI~k2AqnWW{s9~>Ye9s!OYv%P}UW>T;pmo#}+U^q- zkbse?aPCQUH11rRycXzY%t?2JInVyLpVsH|28B729GL99VANQ$GZxzpD7F4{k%s1Z z*bbCdDCTjMiX6G^Q;_R;ob}x(Zp{sMI`(yqX}R-V3vK$=#9)e`?k$2}OE4aQy=G zqC4LK)qJ$%Ccm{`{s`Wrj1L!!Qpl z$sZbs(@v^QC28}4WXvB zhFk*X=FJCGl`}}uiS^1D9RP{0pnk+dpH<;4C@{Ep`z%xfPQsE7hVDjQBvlM$`L`z5GZ zU)y%cQ!Lx6m^>u9D4#e;6|G#|KwK~KwOXj!d})fg|Jk0iROmr}dt6Z*g1EbHt(8_V zgT`w|qpH4jklb(6k$YZDd+k@<6q+t(Gn9f47k$c6xp5-@rKHcO(FoDM_kay*YaJBs zO#q?)`Hd%fPBOZW=8hZ$w9d5v?3BrS|xISz)?t z%T^0_PVCD>*Tdy}0*f(RDH-uyAl5)EPXm^jvile7Wybnu0;p#B@cV24BR||DcE5rf zMa4)rM=APA-5)B=Ab#bF;W9)K2NHJJXWE!TzjO;%MTd1o6!WjvN4379{XV9g%|2uJ#)B_|haM;MY$yC4Xw;l*q6!SB zb^26$zLnLD)GG+*r36c?Q!h&v~~YEMyCMDhik{-nsm77H_mUNW{v zV%_Mf>tn%&`U?4QjX!N3BRRCBh?WgSS1xGZd~>FP#^x)&2Gu_AYt%*C@WcQJn%GIW zACp~oFEs5du*S3fl-v2n7G5}@{n;$Lzt#hGU&JvPrPE}Y`OKp5bAUwpP`dpC+28pmMRC{S6Y?$tt-+MqbA*?#uS=yGdi>%eZ{-z-Mc&mLL$_6met8x%)(Ba4J!R zq+A<9Py;!0bExU7$!yoNbISUllRp#D_sAjw*z(E`@{{xpFg32{!V%YI$!1ZkXvQme zpQ=pv1k&1m1F6Z3?vGdfI*qfn;2+fAyis#6<6`{$0lMiBog`YTN|!Wa7q6%t=L!n& z=m0#WSlF#jY5>2cYi}f%v(=NPXSVW7KgbmU#FA6hw8}2D7dcu~$ohka@(%$gIb+mmZ?g}(@l8i9MzL~?{dOqtBd9-1{laBaqr zK;yZa{{fLCrfo8HrFavL(hT9q90p+ z)~mA@Ja_!}e;PMF1J|8mcy-0^Ri)6Qf1b1944FQ(kbc>-bI){L#EY1%=56Tgg=;Uc z|H+6SukV+(1DYlUHC#y-J6O_Rn=-f*7+>XQ2dq8C^8Os4kKMw5PX=3YNd@qtyQsBQ zq{QaN9qL_*kEYF@UocQlrlvrXyPyRHkafTU0Y!q*5r~4BL1r8UCL3~#`8SFVV)r<3 ziL`g2fzyE+^Q`Sz;8(mg!er zFXqJQw&yN}zzpBWb2E2b>ZOJx#VMC?H2Ptd>R zTIQ>r{qRh&b&je1a4JxI_(6}yN;+qnFFkQnAF_v4;Bd%~zGeenhzxa2=qm#gAhLJ( z5KAG?upj0?gn4gN^d;GrO$esBY zHch&FOe3KYBRU2gGLwxByxA0oXp#o|P`||S`wI%%mDdY!S^I@L0Y9|?5JB|SIl|A8 zB16^0JuFH7Xta;wg6sWt@YVj6u8>YTMFlylj&n2cJea#=A(kI074ZIY@9j{T>FkJ+ z<>zXn%g-NQZUeqOHIRYmr>aW*4Qzf*Lr5oO#>tpUUYO@Qb*7`{hjVmg0 zPz?gP2w*IAYpHv6$XR^qt`T57OiVa9H%&F15L;=hMlVI2ujJFPp1hRX4Gj8`k=6)6 z;RM97;>iEB!Qay(#&+y2+Eaw+Rt~2E8g^WVsZJ)hrrBM5;Pi|x%lrxl@8tfrFf|_!lf=KAX^$mDI<$1AAm+dOr6Ei&o){0QY8v-ZYT#R?%@Q% zrv0ay04mDy0&Y#~)hmEpMmd6Wz+JeEOh=e9XRG~3iv%BLuu|91fuhD%dj4)L{QoJp zfrkxKET*P$1LNQ%>A(Z+Ro?xucdoZ!_V1#_&gusuha*nXOEl4 z_h7moc_>VC2+O(ubZh=E{%rn92CQBNND;jfn0+RNZPgeIX5r7D460yH9ts6t$UPvS z;jQaCx54F#Q3AmMVznBG1V3ez(N^6DQmW)Je|byU-k0TPARR013MUBgo#3I-d$Y>2 zvfpqE3kgxnMn{O1V-ZE$2TH!vA-A&!Y1XCOsG@kxIb`=@H&t`uS<0U*`T6-VX}(PJ zkSlz)l;2L(cn=meWZDXeI2?6H^y#JjmB`@!^ zySJAtaigPWar+D;y!R)NS?hn-R-K)lohK|KBlDUQlD+A03UXebQMJ!{QBqPeMw*8# z6=V^uuy{KN4=Q3NM@L7uCwnexC@FAqay|f$Y##+Ag%cA&;mnXH1wVZ>eX`AjVi}$o z%`#rN!1j@`Ky)hM^6c1q+h^cD_`0Z?gXjL`>}^bm-kRNS)a;&@c&x{`vNGfM1+;Rd z1_;WWdGy{d(f@;+7mKb=4Gp$uL+|sCNAFKrebEFLE6!;xPfl92wvvIIVBTED>qpi9 zXx}`lseD1I@1Y0$Or8JEN=ZwTkp0ki)kCzTT(jeyD`e9JJlgrT^q}L>&4+luOfyr4 zDbR1E*H`Z{276K-dB-C8Ge5CSxS~IGJST~eM!3;IRVs2?3Sw1Z(G)lVS$n$F>*I>l zSpzS!z2X|A=#&r|A_i$07_1LCs-znGY(LS{(_{1;E@E+M7GfglWJ(hp^6Kx5YHRDw zXOokY5{fdHr|(@IUJ!isYe-8cI3-#=8_i_DbC!gNO z0Wt9w@q`426nGZCf!;H5?(FRB`TbjOu(It~MdyG%G;!>qFWsS`pc8 zhq|aM#!mVyKmeaWd7S)j9r2!(oV$$YEtQWJ7GCl%wrkz`yzpO_1#_1IC5lsJs5snF zQRes3YyUGumtS{tK>k>Rp#h2cWM4BCbTU!Fd0x|^?rS9RddK`$c&P7PMhMqqFor)8 zK!ugsSPm2SV?#pKJ?gdYc7}JZHCkAtl&FXh%1NIlID%;oVl{WU7aY>2cZC;q=X>P) z70xZp&xFHg`YTTd39UjlUxDr@3RzoQ>z#bCs1-Q5njHTo@V0@gIeh%0l&F(@x+oq> z72J6VG^KT;!v<4?zMrPqt)$=~$6_qv_W4Y~VWYr)tp$QIr9lYVzDCTQ0eW)uG7z$wkc_0)bH8K_@J@ zDB&f=VynsmsRav2v!aL3XY0v+@0u3&n&{u`Lb8@Kgzw+KKT{H`LYBh8 zO`rO06B|{(to?;}xbVoKXw$<~r-x`Ooho6zeE9e!L=h-jt^Y9mH9BgVZ*bQpTAWE9 zZ!*33Ep3dH%W(IrChJ|N=g+gLAhlq%fSf${^Q+sY=f3`tcTm1J;f=F~e>aiuZZTSe z<2qCIxxZiQiN5S?kc)GW;0*yb`MGT3{hau|1g3>QO&1e}RV>(i0z7WBNxTpg;pKu9 zsDPgi6fkfT&zrx#*llyU>%gt3{B-qrTGFse0%XO4&i5Zd!@w&xcCkRnNJar!%qTfv)DJ3{5?Rc6XRqrwQ)7tF<5_ zkel?rUWCS67Il0$5BG(f7W7HsfSEjTrB!EefqUeR1(u8C|+U zkcI38hluJf7^s0={+JQ6lMH&mBWo8~)O}eg<-VHMIajbKcz;e3SAq9B|9XKCadNQ- zyTlvHX(-TEq*#?d)uT1_7T+#+!9I*^=e@7-V#XSHgZt4*g%62^5V1R=#|OrgRd#Yy z!Dc|{^Zfvya*JNS?i;^RH|TDqfHf5V;F8X#rT8D++uPf5s*_-Zm$RFXKp?F*C}ZzE zZx_pf-;JL)dolWXi<1q0RS*5>13^q{X6D)7dW_V$syij~P|aj&$l7Hvr+7M;=OP-rcZ@yvx&<|6HCi z3Rus$NDG<$?}uf@yXF_#GmBpA-u|WPRPqbv(3{*HbG77AHIYcHv#*9?a#-JeKj-Rm zOa+;x>~D(TiknV0Ebi)utP5G1z4O1M2A|IMSDj^321^1hn6GD^rM`y$?i6F2|5bB& zaoj_Svzu7(&Nbkz3vU}$voB>$;E|A&WTToc3+J4Ryu5IDuie8k3!b-FRJm)=8*;S& zcXq}VXWKHtPdWd;=dG-)Ok-y1xHuOXTI0`nc`@ytu!&0E{L>7-?S*K?Drz4mI2?Y5 z+AE!}xSJhf6#zzq*u~f5!&`eR?ukNAGrHBNQz3(m^N@sL_4ITmaH|)=KtCVKvxhuL znUgJTOufIy_wMf3lw&VB)$R)Q^L$PCy&qK@KbYala4_Q_jj9xrnuI#&szw*p(Ns3tcjn$yEr)SdXP?1%7U5R?|GhO_6~WoK3YkBs^&InqRtzyPZga0 zU&o!US-d)9>fRS8mJnm|!E=7G2?!z>H|^Ozg$p(MAGqx~%nxfgc6TOTU)sCTc(dhZ zhp7e)gllE^48)4zOR~0t|9)v=B1h{}V&9Fo7!_tTCWRp(&d`=j1<|3HT@JZLtP6Z) zf_fqj7}NC7jj~U4kc#C5aJ80=*8Pw3D=Vm)h6Z!ZzOS#c)I&A=>+0+4{Q}>D8Os4C zfAB)n!>XpHAhyMqB?DunSd<#ZwZ*QM* zJxqmnb0rgi%w2XtTDZre)ay^ z?gz>OVuaIk-OPq)|#iA!u_Wz2~Jj@$-3|bDm%Gx@WgK6Td9QcWQcwoNz*{-h=MD z6050=baIjx=;Gw!l2B7qW0*C(wY&x22kM#=F$vt;Wlz7p5HfH&=!#()-!i#1@kkP4 zb&WDN#NG*0>>z*nlLZBxIraAhP-*i|V2aRPNre+o$arGIPrX?v1Y;tfUK)x$ih)4> ziBcY1(0YDuZf?D(Ec6qnTRIEW){zRL7z7#whwg9D|NL&{|_`=oG$}} + +## Getting Jupyter Book to discover Jupyter Hub + +As we began developing this workflow, we realized that there was a blocker in the JupyterHub and BinderHub ecosystem that needed to be fixed. We needed a way to **ask a JupyterHub whether it had an unauthenticated end-point for service discovery**. Basically, a way to ask a hub "what kind of hub are you, and how can we launch an interactive session on you?" Doing this is simple-enough - JupyterHub already has a way of reporting its version and application type, which allows us to infer how to launch interactive sessions. But, we hit a snag in an HTML context. + +By default, JupyterHub disallows certain kinds of [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) (CORS) requests, in order to restrict other applications from abusing a JupyterHub's API. If you hit parts of a JupyterHub API from _the command line_, things work fine. But if you do the same thing via JavaScript from a website, the request is disallowed. This was a problem if we want Jupyter Book (a web application) to be able to make requests of JupyterHub's API. + +So, we realized that we needed to make an **upstream contribution in JupyterHub** in order to **enable an interaction between JupyterHub and Jupyter Book**. In this case, it was a relatively simple fix: allowing CORS requests for the specific API endpoint we needed (which is a very lightweight endpoint that is not vulnerable to security risks, and is broadly useful to make accessible)[^1]. That resulted in two PRs: + +- https://github.com/jupyterhub/jupyterhub/pull/4966 allows CORS requests for the API that was needed for service discovery in JupyterHub. +- https://github.com/jupyterhub/binderhub/pull/1906 enables this workflow on a BinderHub so that its services can be discovered. +- https://github.com/jupyter-book/myst-theme/pull/503 adds new launch button functionality to [Jupyter Book 2](https://next.jupyterbook.org) that allows readers to bring their own Binder / JupyterHub links for launching. (this is what necessitated the above two PRs) + +[^1]: This actually required an interesting bit of team discussion that was much easier with a few 2i2c staff on the JupyterHub team. The original request from Angus got interpreted as opening up the _entire hub API_ to external requests (which is a bad idea!) but we were able to quickly clarify and discuss with the JupyterHub team that this was only about a very specific API endpoint. This is the kind of communication loop that often goes haywire when you have people contributing to a project without historical relationships to the project's maintainers. + +As a result of this upstream contribution loop, JupyterHub can now accept API requests at its "service discovery" endpoint, which means that Jupyter Book (and any other web application) can more easily learn about a hub's capabilities and version. + +We wanted to share this short vignette because it's a good reflection of the kind of value that 2i2c tries to provide, given its role in helping to build and enhance networks of infrastructure, domain communities, and open source communities.In this case, we enabled a _cross-project_ workflow that required knowledge of each project and a vision for how they could be used together in a way that was greater than the some of its parts. + +We think there's a lot more potential in these kinds of workflows, and are eager to continue our work to identify and enhance community-centric infrastructure for interactive computing. + From 603892ae1ea2ac3c32ce7afcd173d2a657128bd0 Mon Sep 17 00:00:00 2001 From: Angus Hollands Date: Tue, 21 Jan 2025 20:12:26 +0000 Subject: [PATCH 2/4] wip: change all links to JB2 --- content/blog/2025/jupyter-book-cors/index.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/content/blog/2025/jupyter-book-cors/index.md b/content/blog/2025/jupyter-book-cors/index.md index e127e4340..a69a9bf12 100644 --- a/content/blog/2025/jupyter-book-cors/index.md +++ b/content/blog/2025/jupyter-book-cors/index.md @@ -10,17 +10,17 @@ draft: false A key challenge in the open source space is that projects are often independent and autonomous, with relatively few formal ways to collaborate and coordinate efforts. While this usually isn't a big deal, it means that there is a missed opportunity to grow the impact of an ecosystem because it requires coordinated development among multiple stakeholders within it. -This is one of the reasons we created 2i2c's open community hub platform. By deploying a single platform that utilizes entirely open infrastructure that we contribute back to, we have visibility over a variety of projects along with the need to combine them together for a specific end-user outcome. One-such development scenario recently came up involving [Jupyter Book](https://jupyterbook.org/) and [JupyterHub](https://jupyterhub.org/). +This is one of the reasons we created 2i2c's open community hub platform. By deploying a single platform that utilizes entirely open infrastructure that we contribute back to, we have visibility over a variety of projects along with the need to combine them together for a specific end-user outcome. One-such development scenario recently came up involving [Jupyter Book 2][jb2] and [JupyterHub](https://jupyterhub.org/). ## Allowing readers to "bring their own Binders" -We've recently been working to integrate [Jupyter Book](https://jupyterbook.org/) workflows with our community hubs for a more seamless experience (for example, having book pages link back to interactive cloud sessions that allow users to interact with the content). We imagine a network of Jupyter Books that all build upon the same core infrastructures (JupyterHub, Binder, etc) for cloud-based computing. Our hope is to allow a user to _bring their own Binder_ with them so that they can interact with another book's content with their own cloud infrastructure. For example: +We've recently been working to integrate [Jupyter Book 2][jb2] workflows with our community hubs for a more seamless experience (for example, having book pages link back to interactive cloud sessions that allow users to interact with the content). We imagine a network of Jupyter Books that all build upon the same core infrastructures (JupyterHub, Binder, etc) for cloud-based computing. Our hope is to allow a user to _bring their own Binder_ with them so that they can interact with another book's content with their own cloud infrastructure. For example: - A student with access to `binder.myuniversity.edu` could read a Jupyter Book created by a professor at `otheruniversity.edu`. - The Jupyter Book is defined with a [Binder specification](https://repo2docker.readthedocs.io/en/latest/specification.html) that has a recipe for re-building the environment needed to run te book's content. - From the professor's book, the student can choose to launch an interactive Binder sessions on _their university's Binder_, allowing them to interact with the book's content on their own infrastructure. -We want a workflow like this to be as seamless and un-complicated as possible. We also want it to follow the same fundamental workflow as the [nbgitpuller-based launch buttons](https://docs.2i2c.org/community/content/). Along the way, we realized that we needed to coordinate development across [Jupyter Book](https://jupyterbook.org/), [JupyterHub](https://jupyter.readthedocs.io), and [BinderHub](https://binderhub.readthedocs.io). +We want a workflow like this to be as seamless and un-complicated as possible. We also want it to follow the same fundamental workflow as the [nbgitpuller-based launch buttons](https://docs.2i2c.org/community/content/). Along the way, we realized that we needed to coordinate development across [Jupyter Book 2][jb2]], [JupyterHub](https://jupyter.readthedocs.io), and [BinderHub](https://binderhub.readthedocs.io). {{< figure src="./images/featured.png" caption="The three projects (Jupyter Book, BinderHub, and JupyterHub) that needed to work together to enable 'bring your own binderhub' workflows." >}} @@ -34,7 +34,7 @@ So, we realized that we needed to make an **upstream contribution in JupyterHub* - https://github.com/jupyterhub/jupyterhub/pull/4966 allows CORS requests for the API that was needed for service discovery in JupyterHub. - https://github.com/jupyterhub/binderhub/pull/1906 enables this workflow on a BinderHub so that its services can be discovered. -- https://github.com/jupyter-book/myst-theme/pull/503 adds new launch button functionality to [Jupyter Book 2](https://next.jupyterbook.org) that allows readers to bring their own Binder / JupyterHub links for launching. (this is what necessitated the above two PRs) +- https://github.com/jupyter-book/myst-theme/pull/503 adds new launch button functionality to [Jupyter Book 2][jb2] that allows readers to bring their own Binder / JupyterHub links for launching. (this is what necessitated the above two PRs) [^1]: This actually required an interesting bit of team discussion that was much easier with a few 2i2c staff on the JupyterHub team. The original request from Angus got interpreted as opening up the _entire hub API_ to external requests (which is a bad idea!) but we were able to quickly clarify and discuss with the JupyterHub team that this was only about a very specific API endpoint. This is the kind of communication loop that often goes haywire when you have people contributing to a project without historical relationships to the project's maintainers. @@ -44,3 +44,5 @@ We wanted to share this short vignette because it's a good reflection of the kin We think there's a lot more potential in these kinds of workflows, and are eager to continue our work to identify and enhance community-centric infrastructure for interactive computing. +[jb2]: https://next.jupyterbook.org/ + From 373d3b96e0de27c38f6b96ea7b4cfc3e2f9d26ab Mon Sep 17 00:00:00 2001 From: Angus Hollands Date: Tue, 21 Jan 2025 20:15:29 +0000 Subject: [PATCH 3/4] wip: small typos / rewording --- content/blog/2025/jupyter-book-cors/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/blog/2025/jupyter-book-cors/index.md b/content/blog/2025/jupyter-book-cors/index.md index a69a9bf12..7df39c647 100644 --- a/content/blog/2025/jupyter-book-cors/index.md +++ b/content/blog/2025/jupyter-book-cors/index.md @@ -36,11 +36,11 @@ So, we realized that we needed to make an **upstream contribution in JupyterHub* - https://github.com/jupyterhub/binderhub/pull/1906 enables this workflow on a BinderHub so that its services can be discovered. - https://github.com/jupyter-book/myst-theme/pull/503 adds new launch button functionality to [Jupyter Book 2][jb2] that allows readers to bring their own Binder / JupyterHub links for launching. (this is what necessitated the above two PRs) -[^1]: This actually required an interesting bit of team discussion that was much easier with a few 2i2c staff on the JupyterHub team. The original request from Angus got interpreted as opening up the _entire hub API_ to external requests (which is a bad idea!) but we were able to quickly clarify and discuss with the JupyterHub team that this was only about a very specific API endpoint. This is the kind of communication loop that often goes haywire when you have people contributing to a project without historical relationships to the project's maintainers. +[^1]: This actually required an interesting bit of team discussion that was much easier with a few 2i2c staff on the JupyterHub team. The original request from Angus was interpreted as opening up the _entire hub API_ to external requests (which is a bad idea!) but we were able to quickly discuss this with the JupyterHub team to clarify that this was only about a very specific API endpoint. This is the kind of communication loop that often goes haywire when you have people contributing to a project without historical relationships to the project's maintainers. As a result of this upstream contribution loop, JupyterHub can now accept API requests at its "service discovery" endpoint, which means that Jupyter Book (and any other web application) can more easily learn about a hub's capabilities and version. -We wanted to share this short vignette because it's a good reflection of the kind of value that 2i2c tries to provide, given its role in helping to build and enhance networks of infrastructure, domain communities, and open source communities.In this case, we enabled a _cross-project_ workflow that required knowledge of each project and a vision for how they could be used together in a way that was greater than the some of its parts. +We wanted to share this short vignette because it's a good reflection of the kind of value that 2i2c tries to provide, given its role in helping to build and enhance networks of infrastructure, domain communities, and open source communities. In this case, we enabled a _cross-project_ workflow that required knowledge of each project, and a vision for how they could be used together in a way that exceeded the sum of their parts. We think there's a lot more potential in these kinds of workflows, and are eager to continue our work to identify and enhance community-centric infrastructure for interactive computing. From 77b6ff4219f1549aa012b9bcb60abed324d85a3b Mon Sep 17 00:00:00 2001 From: Angus Hollands Date: Tue, 21 Jan 2025 20:23:34 +0000 Subject: [PATCH 4/4] fix: correct image path --- content/blog/2025/jupyter-book-cors/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2025/jupyter-book-cors/index.md b/content/blog/2025/jupyter-book-cors/index.md index 7df39c647..e759eb7c2 100644 --- a/content/blog/2025/jupyter-book-cors/index.md +++ b/content/blog/2025/jupyter-book-cors/index.md @@ -22,7 +22,7 @@ We've recently been working to integrate [Jupyter Book 2][jb2] workflows with ou We want a workflow like this to be as seamless and un-complicated as possible. We also want it to follow the same fundamental workflow as the [nbgitpuller-based launch buttons](https://docs.2i2c.org/community/content/). Along the way, we realized that we needed to coordinate development across [Jupyter Book 2][jb2]], [JupyterHub](https://jupyter.readthedocs.io), and [BinderHub](https://binderhub.readthedocs.io). -{{< figure src="./images/featured.png" caption="The three projects (Jupyter Book, BinderHub, and JupyterHub) that needed to work together to enable 'bring your own binderhub' workflows." >}} +{{< figure src="./featured.png" caption="The three projects (Jupyter Book, BinderHub, and JupyterHub) that needed to work together to enable 'bring your own binderhub' workflows." >}} ## Getting Jupyter Book to discover Jupyter Hub