Show the 33 OpenVibe sites that went live, an IT-support plain résumé, colocated
Changes
@@@ -47,7 +47,7 @@ endobjendobj8 0 obj<<--/Author (Alex Frison) /CreationDate (D:20261020184315-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261020184315-07'00') /Producer (ReportLab PDF Library - \(opensource\))++/Author (Alex Frison) /CreationDate (D:20261021063644-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261021063644-07'00') /Producer (ReportLab PDF Library - \(opensource\))/Subject (Data Analyst: SQL, Excel and Power Query, compliance reporting and automation) /Title (Alex Frison - Resume) /Trapped /False>>endobj@@@ -65,10 +65,10 @@ Gb!#]>>s9I(4OT53,_0-D9%fXR!i+YaUYY=&7B-`X7F])g3+!i>3Tp54>f?P^XFdXL&_\uC+:B<**rErendobj11 0 obj<<--/Filter [ /ASCII85Decode /FlateDecode ] /Length 1880++/Filter [ /ASCII85Decode /FlateDecode ] /Length 1879>>stream--Gb!#\?#poC'Rf_Z\506]!b(m[,Yo9;e/:>k!7uY_MnCQB's4j*ZmK>Sj*RT`_b&XbBBmcM4L"1TY-dBKF7'VP2=ZR@H^k\=cKNf(6rAkSF,E#<%a?V?4?NCa'Q*:)p2KSuh/'GP!9Y<G*r\pugJ>.86Ek,>^X_Ps/49%F%K%Bd[i.COrci+YY`:/&=%ok&2tW!)9QEc>Sf;RXEm0@F?DB`p&YHJ*4(1+OgL"Xf2pHgE/,XqL\#YJaR%[brCU:Tj5/1H9Y?YY.#%<M=ArWp)X%?&"52/;n[5[;cZ$@%p78^N+Q*GmYZNS(!rc\A,poioB,g;"BSd0B0ogFF%HNSDXlZg:rOse1'XVTtq:'m"@,'H1T4C(]B2figkDJY;AXaFH*^0D?O'Q]iON5ADXiloG04(a>g7U&N2WYNRI>V]l+>D/#>*$\23LiCUPa($KY5c,0_$cQ)(OGsT4HbNkt'[Reo1H\[sCZE#fIKL!]S9humEL)`Y;odeK-c:!JSf1lc;MsaCeKZ1nQWfl!OGDPbA=#VT>%t=MWQlSQMi3<=dQ(Am>>eZ9Z;;(QQo=7aRbO3$jrq36.!JlK8Ek2$W!#Nf6>-4I9<?M_:Y."mbgJ2eKW<Gm9&WpZS>gXj/h]^0$g]]WONgYSkM`:;Q\pWphVf+DZk1Y/eP*-:Looj4o*e*.)&=lR't'LT>-lo[N?g?F(+eKV\Tn1q?_^d?h"pT?i>aXn>4XW5LXde]2SI_d,W:mM'JTrS-bBH(R;iRZ*St&R#&TT9/rXoUL;28b+ZWs_LlBHCUl>83Db`8_G==B4#LVLNI`gcbI_PnI5m^2`A*-3&\d`:U[p+Ld0Q;+BcoHK%6OC52Tfc-m(m'r:&SGa&$;I`k$EiCZS0te4kZ+W/hk:OO5UW3cgOtgEEU4A5&l.2_TKtc"pYD%2g=;5=>=Lh$q2_a$1P7&/1uiSU"<NVQU=6sdoEIs4&@%Z?">(^G^__<DV3<@8MW:(rZ?=,YH``VKJ6(@KUMe&PmJ`fc5:`NC+T<:pQh2nQ[cVooGN(Eo(9/E.EHH>oM+uMG@Nc_SK[-)%Z*RuK9Qk8e)B4gNF.IP%a6gJWguYZ<[\EKSZM5b/1S@kfQD`))877SCg,M^3s2OA<:9['XUI8IE-C1<9)L)C&Kslr3]u&U;G,ushh=n=Y:r3C3G7E[o0YRUrc<gC>hheu":)l.i0qP^WC=W*"4IV8Uk[nl6%S,!*\bh#M$g>tF(Z5%@npg6(8;3NhXO5qW,Y?H4_t<pMq0kB:gQ\!BeZR?!@Gs<RFCFECE]0VJeuh2GL#mEVb>^X)QfH]iBE29Xm%$e;gGiIM%N5eMk78d,dTonjap<dHHZp^FgT1)@qTjII.D/C1HSt7ZI/ETN&/C#2W@e_sL,&=D(e[gSr":j0H4D<&]&%.ni;Go>&9.YG0IbcqS%P-MBFFA$$1-Pjk=J]]!F9M%aq5tZ82,WJL_VOr'I=\ao@o]1_DgLWUB>iF"OLijHG%Qm`r40f:`EaAE@:g"32!BjqRSD1])pI"b$@Y2Gno`ihLVCbba@9Mhnkl8+RM!;`25N/7XZaMd>VXUm8k47DGA)fN%M*fn-ng'gB_U!kC*[#!=HP/GcQIkV2N`V@6ejXI^MQ!Be;u)aa"A)<D1-pfKG0)=,<&PTL=e-$b>'.?A3WN&A=<3NmDT5_qE$%qhc9gr@PMXMb:F)q]3p;Q]B:6p'n.1BkX&8lBP,lg20HsMpT.95[C.s@07:9VZ&LglU&FKR#DDUi+T2-k\Y;;,K%E;/9D-Yg?q,%fUCc6K7`-+%&Uf`9+*R93!-V$7r3c4@O\MThF"6P@:1=-7:@<sq_jpQ:U+B+FL`o$PM[GbWs^#$6HBH&^DAjjq?LK6m(<~>endstream++Gb!#\?#poC'Rf_Z\506]!b(m[,Yo9;e/:>k!7uY_MnCQB's4j*ZmK>Sj*RT`_b&XbBBmcM4L"1TY-dBKF7'VP2=ZR@H^k\=cKNf(6rAkSF,E#<%a?V?4?NCa'Q*:)p2KSuh/'GP!9Y<G*r\pugJ>.86Ek,>^X_Ps/49%F%K%Bd[i.COrci+YY`:/&=%ok&2tW!)9QEc>Sf;RXEm0@F?DB`p&YHJ*4(1+OgL"Xf2pHgE/,XqL\#YJaR%[brCU:Tj5/1H9Y?YY.#%<M=ArWp)X%?&"52/;n[5[;cZ$@%p78^N+Q*GmYZNS(!rc\A,poioB,g;"BSd0B0ogFF%HNSDXlZg:rOse1'XVTtq:'m"@,'H1T4C(]B2figkDJY;AXaFH*^0D?O'Q]iON5ADXiloG04(a>g7U&N2WYNRI>V]l+>D/#>*$\23LiCUPa($KY5c,0_$cQ)(OGsT4HbNkt'[Reo1H\[sCZE#fIKL!]S9humEL)`Y;odeK-c:!JSf1lc;MsaCeKZ1nQWfl!OGDPbA=#VT>%t=MWQlSQMi3<=dQ(Am>>eZ9Z;;(QQo=7aRbO3$jrq36.!JlK8Ek2$W!#Nf6>-4I9<?M_:Y."mbgJ2eKW<Gm9&WpZS>gXj/h]^0$g]]WONgYSkM`:;Q\pWphVf+DZk1Y/eP*-:Looj4o*e*.)&=lR't'LT>-lo[N?g?F(+eKV\Tn1q?_^d?h"pT?i>aXn>4XW5LXde]2SI_d,W:mM'JTrS-bBH(R;iRZ*St&R#&TT9/rXoUL;28b+ZWs_LlBHCUl>83Db`8_G==B4#LVLNI`gcbI_PnI5m^1)1Tpi,D)fqSh#\"4!Ma-icJe(.9"5TDj`0k$J=AZ[3'5Sf8:hRSE#"`&4L`!jY>QtJ%Wp',eUk+bV1A"U84E7uWBrUF$tn")YlX`iLV')E3\Ig#G3)*EJD)37]Bn9FR=SJG8!2-@GBL=NpXB__W2^<kUbSP12,*67uAXXdH+CV;"n>@faS\eVQ[qhIrGXH62-pM0b9ZFc@SRW$a3M9c0+W-YtldF<:c2H>rsKlBQq)KSfpcB?^5fla-*Cdd^:FlJ)p\*n'NQaa!KMV<hYig]G]J7`4lSjGK3J46&Fj!`=S)m?"q_$P[Q&oAYP=FuiQi"<I6\h[I_$?tsMZ')&/hq#)"#oA][op=^Sp^5M3Hg,m+,OTFgERaX$Gg:I]<TD9mBJr&LS&t=b*).sGmLOIfTLUM;I-2$_s2j"?'lM=$7Xnd/,Yam>e,+XEqs!Hb;d*p8]1SiQL%?;2Kr'^Lfn:\U]D@<uQIDfc;t[(qNaL81!AJY8G:mHE@rt!rBJbC65D[6NG?G5&,9/K-c&Le%YMRao5O7:(n1J^!e[u@]h:LCJ<$:1t\j06i`R*>t.oDS99@,6U3Lj(5HDc1CpB+,U_#6X:Ui:,9nR@fZ/]>g[Y^aui-Wtj$FKsjS8!ktS_$IUp<k&iAP\^KImq1tqhi)')%da-)5c63tnNIa,pDY2>D0uB&T`CVAJ@;n#lc/_U$9StP2_?37<Od[+#E&@3@t*lo$ZBL,[*uAnS)X>aor7^S1R>ka?YR$]2&3_X[]Fqq1VaB:,HqiOq[K>+./4G6XU^5S/k-q;!Qj<reIH`"[4L,[k;e2q/HN=jpFnp=+\nJW&iI6RjDk?L(#nUHoMTEa5._gEB,B1g.q\/B\(pg5.bX7W2`/ic3\oUG2mgl-Tliu,=#rcc[jh8.6MF4UFcGgD%6/HuqO^Tc$O&b(1+m*gUTn!q\&%]t$kGS-clJ?d]Ch\'B83Gr$AR.eQi`$o/^WG6#iOthYmJR/V7bZ3&#&-?JW9.6Kn1fCgMDa!`JU)3jfVESIX;X-/pmp3+X=r*?Jt&2+1';[:]~>endstreamendobjxref0 12@@@ -87,7 +87,7 @@ xreftrailer<</ID--[<4db2b9d6052cdbd87e4d08758dd69ff6><4db2b9d6052cdbd87e4d08758dd69ff6>]++[<0708869bd847a2f084ce65d9fc9de7c3><0708869bd847a2f084ce65d9fc9de7c3>]% ReportLab generated PDF document -- digest (opensource)/Info 8 0 R@@@ -95,5 +95,5 @@ trailer/Size 12>>startxref--6393++6392%%EOF@@@ -47,7 +47,7 @@ endobjendobj8 0 obj<<--/Author (Alex Frison) /CreationDate (D:20261020184315-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261020184315-07'00') /Producer (ReportLab PDF Library - \(opensource\))++/Author (Alex Frison) /CreationDate (D:20261021063644-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261021063644-07'00') /Producer (ReportLab PDF Library - \(opensource\))/Subject (Software Engineer: full-stack TypeScript and Node.js, payments, identity and live streaming) /Title (Alex Frison - Resume) /Trapped /False>>endobj@@@ -68,7 +68,7 @@ endobj/Filter [ /ASCII85Decode /FlateDecode ] /Length 2981>>stream--Gb!#]flGh,(4FM1gnu%aN229Zih`8Xfh%]S@RX2SI`Mmb66b,/_#U->FnNf$\KE3!e*\oh;5tV6CbF@bQZLFU!p\bmn@t`Wk^6,Y?Nh-s1ga/-n+:=C9pbGf?X<kXbJ=`#O+Yp,&0:8Q'+XR$R(iQ8<\H./YT"[:@F,G#pZU+f*af84IPc*i\iZ\]3gQ>oD2[PG6I$dM$J2#qLVcT?LU.NCb";GWF?@)5r(Dr')(T-<=gVioNEW(DEg[Y_L&49k6CLYKi8qLOCf6i$J_u91IFk3_;<a7*?N*:_heE'33kDl5(q40>$O9%FHTQ.C^/FboMsSGERDg`rVn.hRWA"qeU*i[Q(/9b5Ue>64gX^4>[email protected]>lZL,!G2:ka?iBrWG%'R010mGKHjShe[IGl&'/[j#MRBnVCA_MiU/<9:d.t0&0Kea^pCDPr:\g4W-_nkHrhPZJ<PEK7DpdqXpL-#IPLK/!(qq%L/8/,jeD?]="bTQH9o>V1<aX-nrj1_Mk]:]jm,u5Rhe1p+Y7mIMHF8b2>Xpa=>fHnJe*Q5+G,2Gg<?dJL>tluL[+=AWRHjG_+m?"^/(+kMBVdC[Jpp"dg[5!,<.6KAVp,Ec@O!**N1csmOiOgW<U0Jb1#0<tb:<Lk-Kq+^K72-@33pCM]2TVr-Hng;d^F'8;RmrJ`lb"JQVn]h@7K!cY^8Uu4Bp"$Hc%!#]aZS9,+7k%@]@VD=[a$i7RS3Q1mUcP3P&9m/QAb*k#XZ;e\*dn=Y+Bpd)*`:"=)=;n7\d80cZRZ@cr8VWlb?oEp;6.mBqftI45=1bPak\Z(noX486;k9",]l@?jl1P*\#Q5XG6C8f6P6I6%T?9K"N^>TDiX0tL0nN@%^l=09qpBdJTN7gC(@^nuCTo/jKsEJ2eNKK"8kr7!qJP)V4b0eE/NeaP*?UYs]TR*ekd1aD=1U0G*bd19aTmECfP163'RbX3H-HAla#Eo13Ii-;ahe+:beg<MRiI:[4uH=+U%Pp"WOCIAQW8*BT)A<%XgcrnlD6Z38Q77\aH;_P4.A`R':(@<mC:)8is*EU?tOI5DJ$fMED!4Se@aJQf3AEVkQKV#To3fuLC<K=^GaJ#"mJ^.39q?)S+0>7\gItH6Z$f(kin\0S<#Q<[XGb?`YS9=.BlSZm)L0NCp^Nc4Ti!dYap/K&3#pH#epMYtn/!r[9;Ak!%;1tma>?Du+.Y2]gI-?9e0#<`)V02oK'cCj?'hK-d74N?4g'&Bs!H-=Tq0,:il(cYHh,<?._YL2Tf_VKncSpDXL>o7CNfCfmnZT0jn1$hfP9M6b,d5<bQptM(0LNBC-tqqZ*Pb!A_Y[)EC:9;=;ebMsN_OW#Dd&GQ,%j0^IElWO7c%Wc1lsU.Ba#;I<^%BB%'s`,*+RPTX`>5VP4J2+CkbH_LUaNP();@A6D]#/LR4i[OSK+Ci]'n3`7#g"Fq,nf?g0>d3l*]jnm83+0O2q==ZD9p"b)4foo(JCd.d5+\7'r@iubDY^&^gAJ:hQ66"ImO^#%<qcS(,)SLM(-o"52=E=>4U&4jr*:5,q,Xb8g;86UiY&L@kh-"mkY_1%9l<[IWIS/?H6W#ipTc\GIga;4TmKS;*4LL24MCklbM>Uj5(!N'1=]g?GT9%P6iN(>B2^Z3TgLN%-'WU@n"AV-=DQgI4Yp_Y0EVE,?KAALe9,lG0[0F[:u,FHd8QF4A*.k\uW@=U&[pUI`[1G6A6pa+O)>D-IW=XQ8dl.I<B$,@%t\TLI)*@DmRo+5cP!g`j=nEQPcNp4<d+#8*tGuXLaDC^-TNYX,93%Of:$C4)`pTLdWrrt_bH25gkp>aUL._m&YD:*#-TY;-gQbpQ7"umPJ\\b*)?Z]UX"`h%n$PmG2R-F5ahYWLC^XM_3od?p(\_&a?FEf3c!-Ep[RL06.F7st,]fs`WE=.f8J_;P]WYhJL;FrqB#>D8j[XTSHWcY]*G_jmB3Of@3.#(F)A9AgZ<'_7@YB7u.['/>1?>I]@i:QhSrks:B9X?LK8:5,cbCU+NMDa9_hWb.9"%Z>2.\M&uZj_do$&%-k%pJGO:nAl^R?9;.BY:0ce9L&\[)m(=_bkN8-.eulZ/aZ[mi+q5m54,h<;a>g:V>a_c\@$94;mKdp>,"GYZ3VJQ`m[(?F@/`lc\hrnq&4;S%#nr29jsXB-,J>YMSh=omci77IHXWOQmg*>tSi)j<0)k:J']HUM_#c'ls#:ORiZ;qNG+(c8?;j+*fMUO3WR=HhGcU`<6q!WamVZc=f7g#P[)h\XN:i]TnE>HsA1c<Ws*&QrQabKe@&eO+Nhok$h-:8#[t?bpNSK][hp-Yq+a8&*:7Z`""9A;Z]oXf%!#(P!4VrBaF44(]4tL333qQ=5/.!3+<@XTjfVZ>ZKk;`:%m%JhnJp(@-s+\*se8L^t>/f#J8Ng3^eh\lLG_%iq1So-G+I4W[4'*Fl3Zp9dFi$"r>QW?iCh/ks\PgWC;U=0Eb`pBtDc[Gn(;W\T`QLaE-9G)&:N.NZMU+<nXEIB3R0igl!6K@0$!%QZP!A=.V6(i=!TeNLF&-)]Wj?p96ji"^*.>pRE$:R)3fUuu+U!Zl8ahY\r62ld(r5?+@$fiV#n9TA^j&3jBaq;[-W)`_O\?IT?nd46!d<0LfgOD9ECJs5]%CdoD_;$_0-(V7!(fuR-.f,b=VDEc\?#uBr2nGCV`ksi(9!s^!RU&H>c%.np9Z>2E-EUPic;3^Mh[!t!op3%Uf7ks\Q!QIjLGa@sN(OT7d&3btba?e#?-uR1m@+bGJIIDZ[_97atj3deik'b.;W7t,IIf^%%V\%arE!/N-;*jkj"kBdn4P'nHUeU'4no?J43p"8o%]W[#ED"o!c[IFnW(]ih6"WAUTg0ZOI!jH$_OoTLQk^gcgCF&=cj?XI[pp/gg"qk0&`Nse3I;IP.UO)km5LAZ;Z!EY!0-Len6!X?,)kL.UF+?4Oie)G/cY6^HLX4or(:2~>endstream++Gb!#]flGh,(4FM1gnu%aN229Zih`8Xfh%]S@RX2SI`Mmb66b,/_#U->FnNf$\KE3!e*\oh;5tV6CbF@bQZLFU!p\bmn@t`Wk^6,Y?Nh-s1ga/-n+:=C9pbGf?X<kXbJ=`#O+Yp,&0:8Q'+XR$R(iQ8<\H./YT"[:@F,G#pZU+f*af84IPc*i\iZ\]3gQ>oD2[PG6I$dM$J2#qLVcT?LU.NCb";GWF?@)5r(Dr')(T-<=gVioNEW(DEg[Y_L&49k6CLYKi8qLOCf6i$J_u91IFk3_;<a7*?N*:_heE'33kDl5(q40>$O9%FHTQ.C^/FboMsSGERDg`rVn.hRWA"qeU*i[Q(/9b5Ue>64gX^4>[email protected]>lZL,!G2:ka?iBrWG%'R010mGKHjShe[IGl&'/[j#MRBnVCA_MiU/<9:d.t0&0Kea^pCDPr:\g4W-_nkHrhPZJ<PEK7DpdqXpL-#IPLK/!(qq%L/8/,jeD?]="bTQH9o>V1<aX-nrj1_Mk]:]jm,u5Rhe1p+Y7mIMHF8b2>Xpa=>fHnJe*Q5+G,2Gg<?dJL>tluL[+=AWRHjG_+m?"^/(+kMBVdC[Jpp"dg[5!,<.6KAVp,Ec@O!**N1csmOiOgW<U0Jb1#0<tb:<Lk-Kq+^K72-@33pCM]2TVr-Hng;d^F'8;RmrJ`lb"JQVn]h@7K!cY^8Uu4Bp"$Hc%!#]aZS9,+7k%@]@VD=[a$i7RS3Q1mUcP3P&9m/QAb*k#XZ;e\*dn=Y+Bpd)*`:"=)=;n7\d80cZRZ@cr8VWlb?oEp;6.mBqftI45=1bPak\Z(noX486;k9",]l@?jl1P*\#Q5XG6C8f6P6I6%T?9K"N^>TDiX0tL0nN@%^l=09qpBdJTN7gC(@^nuCTo/jKsEJ2eNKK"8kr7!qJP)V4b0eE/NeaP*?UYs]TR*ekd1aD=1U0G*bd19aTmECfP163'RbX3H-HAla#Eo13Ii-;ahe+:beg<MRiI:[4uH=+U%Pp"WOCIAQW8*BT)A<%XgcrnlD6Z38Q77\aH;_P4.A`R':(@<mC:)8is*EU?tOI5DJ$fMED!4Se@aJQf3AEVkQKV#To3fuLC<K=^GaJ#"mJ^.39q?)S+0>7\gItH6Z$f(kin\0S<#Q<[XGb?`YS9=.BlSZm)L0NCp^Nc4Ti!dYap/K&3#pH#epMYtn/!r[9;Ak!%;1tma>?Du+.Y2]gI-?9e0#<`)V02oK'cCj?'hK-d74N?4g'&Bs!H-=Tq0,:il(cYHh,<?._YL2Tf_VKncSpDXL>o7CNfCfmnZT0jn1$hfP9M6b,d5<bQptM(0LNBC-tqqZ*Pb!A_Y[)EC:9;=;ebMsN_OW#Dd&GQ,%j0^IElWO7c%Wc1lsU.Ba#;I<^%BB%'s`,*+RPTX`>5VP4J2+CkbH_LUaNP();@A6D]#/LR4i[OSK+Ci]'n3`7#g"Fq,nf?g0>d3l*]jnm83+0O2q==ZD9p"b)4foo(JCd.d5+\7'r@iubDY^&^gAJ:hQ66"ImO^#%<qcS(,)SLM(-o"52=E=>4U&4jr*:5,q,Xb8g;86UiY&L@kh-"mkY_1%9l<[IWIS/?H6W#ipTc\GIga;4TmKS;*4LL24MCklbM>Uj5(!N'1=]g?GT9%P6iN(>B2^Z3TgLN%-'WU@n"AV-=DQgI4Yp_Y0El:<Uj"X!\qnDi*8Ycf^=?oa!HQL\(Y\0gfj!Ph7`j5NDO\FP70QX$,!!K(T'S`k/CB9/-$^cEV$efU/uS9U,h6aUtC8H-6*X*8kLUArefaQ1V,;:n7IXn9Rm>,]Z_XQ5j"8.rVm^V5$o)t6,Sj[6r>O&)Ar!-br["f"5.qGrtg$3TT5^hf7r$B(X.d<TF@\Co:^8>O4,VUKm:mn\9.:<,/VT=C3M@ahR0iM$#Ia;%`b.Mt"dg$_)uni-`j.3R1GB!60Q,)@!b-0`o$0X]'Xrfr/6db/g$+]^,Ti$.BZ(b4@)eS2Z0"]`p_dP4\I^3JO\XXPcPe)q,7'E?<2c,thdnWfq&$a04QepJijV[(]^+A&9,!9TQo1*gJ.%sD/ABY>'HH&>^:6I**6nWr#dj,q[9-ED8"a3Z]0>aM2)NcuGWD(nPgdk'ZYnEZ6%nWNW/)s[CkA?]:kGDhs,<nmg`^;f/Ye^?(R0?<\-$^sb-hVr;$1j?-pGt;_`HHR,Ts68hc\Smp&WH6"#.Ab^]K+%rK-(1Bc\o"BGaOt>qc5Xjg:MM%7f+8dJjLqc\3[9+B`CCjN'1=C0P-%J>UZq[sgjAq\C>qVN<\C9@E@jjf'h#E=(+FMD\tDVZ$;0$AM`joZV5doXT*TQ1;C<*rY't"DPhV'/4")p]#uKS_<Wd&,qA>Bm+i)!%n5E`RY[2Q]mIi^%LcOOLMRaXE<K1K%^Y*$'c?rSnX>fFh_D="k5$rP;T/lsemhGri>.LFmF)_!$=&A,TGU@uaN4"8jf-3p8S<7TbQ=/4R1-sr:.pF]7]nTeI08(L7MOS@Yb"+-.RKQ=:*W?bLGS$KtL]j(;YnF-pG:n1N7^GlMkhH0$@H)]E[&(<7pY\)5S:2E.q%2tF=0thIN>P#F<?ef5Sq"iiGe@m=n$b"+XO@"F9SnX;@m,N"2:ff6r+oBqNt_@qjIE2o;(iKL2\c^;WlZtD8@?92_+FE#p*("+."jS'$XMKOBOn-NCQAYf2P4dZKW"N)q!I;1E22.(JV+Nd;#c[l#("!-feo:R\G(@"W6'5ng-;$GH^bT$PF24%!95D64Ni$8lTM:"#Tko\jIg(0'K>,G0^Sf6[eE#6i8qqunS!Aoo=eX.PjKju561u#6\t293!,fRW1a!pK'HDGT'HM_dO+TUH+B;USatH@#K87=32LH!ANprE<2LtE+>Y.;:`k`a5!J7MiQl@abR0KmD23LUkQFk`gT_+DD!sdO#k<MCS3;a8PqS,q[[ToRW.!0<JAugCGc]m0&]X>(;%mWU87`"4Q@F*?^%`Zj-qWn~>endstreamendobjxref0 12@@@ -87,7 +87,7 @@ xreftrailer<</ID--[<3cb1e6404bca482100e41159cff17f6c><3cb1e6404bca482100e41159cff17f6c>]++[<858ee61722a5f5c2c69b17e80403fd90><858ee61722a5f5c2c69b17e80403fd90>]% ReportLab generated PDF document -- digest (opensource)/Info 8 0 Rnnew file mode 100644@@@ -0,0 +1,99 @@++%PDF-1.4++%���� ReportLab Generated PDF document (opensource)++1 0 obj++<<++/F1 2 0 R /F2 3 0 R /F3 4 0 R++>>++endobj++2 0 obj++<<++/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font++>>++endobj++3 0 obj++<<++/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font++>>++endobj++4 0 obj++<<++/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font++>>++endobj++5 0 obj++<<++/Contents 10 0 R /MediaBox [ 0 0 612 792 ] /Parent 9 0 R /Resources <<++/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]++>> /Rotate 0 /Trans <<++++>>++ /Type /Page++>>++endobj++6 0 obj++<<++/Contents 11 0 R /MediaBox [ 0 0 612 792 ] /Parent 9 0 R /Resources <<++/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]++>> /Rotate 0 /Trans <<++++>>++ /Type /Page++>>++endobj++7 0 obj++<<++/PageMode /UseNone /Pages 9 0 R /Type /Catalog++>>++endobj++8 0 obj++<<++/Author (Alex Frison) /CreationDate (D:20261021063644-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261021063644-07'00') /Producer (ReportLab PDF Library - \(opensource\))++ /Subject (IT and Technical Support: Linux and servers, hardware troubleshooting, customer support and escalations) /Title (Alex Frison - Resume) /Trapped /False++>>++endobj++9 0 obj++<<++/Count 2 /Kids [ 5 0 R 6 0 R ] /Type /Pages++>>++endobj++10 0 obj++<<++/Filter [ /ASCII85Decode /FlateDecode ] /Length 3089++>>++stream++Gb!#]?$?rB&q/qEeD#WsgXtns3;^!.#tnk:"P^`Y3I-bAZ7XM\lB0S9bi-V2/ZIRO\&jdh#N.S=5GV1Wh-7?</Sa9`bC@M\dH_+PHIJTh7$BX=3fOVDhU1lS`Et9%jceLA0(u&@@imA^HDB02T0D*1BbiA+=K>W?9p69,8(?8U[A+M@]JiOF'tl?ElKffk^=)'a]\>sS]sV?gd'%VOb+3GK]Y,Q6h[c3bB[@#4MScMC;;&hNX@?QOFM9]"_RW't0N+FUV!khCM7WK5me,o5_4Zi^!g`hD5N^hV7ODDdou7.dHm*,PCW&8^V6X0MPJ1t\[^T(N=\Se=&%TnJX/Rq!a2r\aVu"!TDfdLR2u,N?di\O!66_oH7)mDD`?M!qOltH."+$lNil^/cdTjYj)D-Z2Xl'n67MjNAg83@1SuY^n=Jo)3aG/=B^$:^-@U\5[c?:51dsgT0U3fYdp*Uf/3^"LDrjusd(,@'"fV91!rkVsY^X9f'AH(PP3WmZp-G_q@/^s)Mg$i<lRu<i!MZI+1DrEc^f,;+K-juG7,8+Q.WJk_jCBLibBJ"qCA;VqU*D4qZ<uE0^EfmJRZ^V4Ai(Gi/:CEH_#iUV.a25r8XBM7oNTa:UlrqJ`)J';AKnMEHdUP&\W^Or[`Y3_3&Ou[%c&3iuQ#e1O1I^X;j@cKg`[:bgORVm:)GN#TkYs$MUe8-AS=i8SgRt78@4K"M`K?`9f4%B#=Su'#V\X).b_QX:pscjGeS&REV5tK$$3sgMF(&A,H7%f1G1L"R;\JQ2V+drWSYjg!nNTV7;fm'TE>XWHR7PnJrWrbc3NWrf)Xl\(f'6@G/PCf:JeFY]j0^`gZSYRGYn<![9;D81?^-ZQ%i2*7DD,A^9EJ[K,-kUNLgrQBNUiEq::3#^e'M8DK#eq&_QF&9fYj=$P<"WiYFUeaf7;s=*>^/BdqT1n:9'lJlKS@f`bOXS)Wfnl*.)/FWR_m%RLQF)MJYJ9WG^n@`S_=Bs6&J!]CCmjgOj,>EN=dh^W]FGlnU9D$D;3HM:m[1#KsSMGag>sr->GRIr@u^5&prBe`_;d!YBn`mP(f[Q[13EEb0DDKPV=A._(t@q_Y6W:PmebOSWfQ!IrnZSc'VbqL!/^Y%2;9ECi_`J\/?#3o>:?`!GGs$Ij4tZ6Us5lrOEF*9m5bRMm1n\sU_UcYG7b-PS08'NHbIhg6s7MeOBL8U;Cc_%D0UgGD#SZ+Mo7<4ubEMV<6aXl%eWl!5>%7%_/ZNeo>AS=JHr]=5ZiA`9W"1Er,m^M0rT0M@r)iU2@:N)+MMbJ2-_R%<?#,*PftpGdQ%_;[NZ=f4'#<qO="jdbRd$O`m@n)\oAHF@ui:@:?bV$O9@jN0HLiBK1<Isb=mZaXR,O%=>+:,Ol]Rb?Csrs2AGHkjg-n"&^.1fZ/U/==UfdHB-!*N+rZcO#IbWUiO7F4Nrr)<h-:/o(IhR4T%@q5#>E&W.7@>/X)&5R7[YP+GU-bK#'k1o.#Aq/9hpIT8CC>LcSrJSP<&g>RTp[.1rXZi9KeB?-d2j7e>L]/FO*4R]cRUJ9nM\E3T,b6O%*;SD`i_:!DfH0UbG80V$rH\XXl&3@h#:U!3Ni5\XEmPgclhfoO]W&>*t8ZgF:O_!j0An5!p-u)47:4k?1r`Z.C^l%>.AOV8m>D^s4F5'2JQI4ltIc'UJNIK*pXd^7=)XYu!Tk?FgAM#[!^/l[TQ01HWd1HL`JdaS3fW;"+>apsgk/A!_*,K+M;],rr60Z:kE6F'*AdQ`T/P*lXHaZ1InS2RY>;>3s5N"t;5_(3hic6%e+OITq_o$e7*8#O>Bdi>B:gj`ZKkVr1)?q]JX67>ZBXU%DF"rQ@h9&&7Mj^_d:17ih\XQh8KE:e\<,r.8MMs!#Y*28WHsrX&\fm!JkWoS;Ks_X,:)X@0`#=eX,jQo`c*0CAecC^M]U;p-q`4ZZXKg/lV`$9C+S4,LQ_grZc`KRPD]d-QjJm:PN!@J5H3L_qA9%;66I`SL5i-X5NZB[]jDDHJ`OSdfGYXI%SEOY@IG:9++O'NQ[tAGu8#TeF:GIJA`sNV;7]^Zuip_I("0BF_VP8ul/mgupNQnmpfNKlHjm5\:e[gbJU%Ff-&(pJE**k^p>9iKQMKmmQL5d;*(FVQN8-'-#gd]a^=N\3_J6.dL.[8L,FA!\\"-o$F@6X#Htus5of\WP7A-;ZkrcpI-2,XNo(3b&-4.GX+V`i7:c;f<-u.F3oE[/P'lg=O4jBK-oRee2,M5>'XMmP2I+_V_V'?\ocJVKN-XsDUUK5h?b1+"%Z:qIui%S7;-tb,j4*Y$QK@#"akmXF]QEdkGESAV&Lga\R\crF=?^Jn"Q4P6KA15qZ[8KHB<$Ye#&)%!\EJlD:cZU/0U3tK`!c%M&+<cGS<g$@[X<CLp@`:]PP"jZ>26(TW]P5'>T_MnZPC(biqH4&*,@^AJf[Acq$fZ==tcO]m1\^Q\&ng/AQNGL.faV2[:Qbs4Cpq^k/poIP3E@;tOjejh]C9N`UKS$#@Q(SbdG@\3dZZ.Z\4DCObjGmeoG?52ok'@<tiC"-m%Md+MhrJ%oa_J!Q;EL<#F['A&#PSr13&4r))dnd^;`eV*AqkTKA!IgbJCDfcq?+')O4^#KWQep6VK.%`@EUSEsLff>;5!C7E&NF2_e>^ll+-[3g/Btn!o*mGDQ,(M&!<S8FX;_Mt&BiO1ukcW+fJ`L6kcZBAtKn-IML#p@l9EjXcro#=mf-Q:l+CjJ>"f_YtO.?Rph:Gd.,+]mC_@s#k4_gim?.^+&R0?C:]M&09YD*t5jc8#P7l;U"rRfBkM(P`.#b:ABP!nAs]t2^I\P[B*&`fmVZd#04XFrL&m<!.0s-V4tM">&4#btG78[LC/#$W77C:@ib5D]8Mo@^?T9oTf`AFPj;.3AA'`]eIZj2U`'^FnkG2j_n+4XZm!:s=g&Rebub>82fl#7r<I-t:KX13VQl"<'F^i&10aPUV(X`f/ctQ<XY(F)BdZ*!>!BGNkI^I:Li8`2+inI^S[lD\[$;HlXm&_Du>9425P"^Kp=F[t1L5$F`A&F!o-AL<Uj3pm2!E^ol`=5gtYk!ICk4q>~>endstream++endobj++11 0 obj++<<++/Filter [ /ASCII85Decode /FlateDecode ] /Length 2105++>>++stream++Gb!#\gN(b6&:Ml+o[@]$f/Aq5M;cQ%!C@A(Z!"*8p15G-X]^]s`69XL'Hb)f8%'$Qi(u[=P#O!u17-If:S1ZS?2Q&>Ip*CHT$`0<#pha'bpaNP"uLZm-aUL1HcbEqi<UeAqV=mW&)_i542T;EIK&IHHPe;J`p\$[Bi\X6*f]t,_tQ2:*e&Uu^%+IV5\IriKlls;VF!pO(UA/29+s]i=0b8E9;asU6Z86R\F0BifmbjUP?7Mt^Rq\b3Q)@!.XRZ8IJ3Ku.S'.]q_X=K9K$\QFaIC$QLNT-p8k%Z@#N9hTmN"44%VRnnEnd,nBbMIOS8]K%M%K7R-JQS-_F-jmdrG7a1:msKNZHV6t+>l67s\hnkQ%?GE:RF<(]*YY)pr<.(+fi!qc/SjSMp!9]7E_)KM?n=4n]YM:>\sWQ_K,Gc9-/psp4SI?CB4PBA?]F#M)r-j3A6N987gP@DVY%R<NXR!T2(\[)H_Pqb53i:/%".+4UdV]\DoG"r.Ro8oInJU$U![E'e2>LQ>tcs0f@lA$3k7`d<Kf!M`#l#U(cKGGAp0P!gj3*P$^#$I?E7*N5bHs6AGBYff->pp5<pjZ(:SlO&9`Kefn[PAnn"GfLCK5,15,Y4<Vb(OOuRCfuAOUVEXqY"/S+37*WR<(4.7A*mj.\$m*Z-PiFc,o@1:2-Ib%GH8j9IP8k23Y$1GCd,FYU/^5:rt*;@0UTH8B:9nC4r.=Vjhg'Z9jrp]il<?]dSS;\*1ehUE'_97ZEB,\3MB.VY9h`^th3EE1G_@P^Zjd*=unXf0Q7),qZ>LJmD-I92<];\(g1k]75qI^M"bt$!t.maQ>j/F!3\Z>RT_J9Cips.6lCX9_eP6$CB:2dOI]Rb,f#S.9nQ_lf@unGA&5P2/aY,:T[o98QZ?i`ahD7+o]BgTE;.P%Q4ee#PU0nVc(jqQQ+j=:fuIOYcK0iiecN]/\o.<0EqR=!kpib2L;c=FbmOmmUU_)pt/?8Z<h5k9j40!\`BKob,ps=kXZ@806DR\O#f6d]U)k;Bc@agN]S]SE,]$pC3"Zjco6"C>>UptLE/@CaYYp`gS<ZiETNm?W4HnE(0[dE<aPlYO7`(gAlGYC`3F\@eiQpd5Q$rFhl@2a1>FU"d_G7m+%m"4ILY6$m&jp.`U65#o`8GNduKb?C5jgpL,GB&W.6#,lXX"QMWCnbR^+bESLVMjS$A(8p4e2ogidXPE&bSH8UMTR$tpDOPW>NQ$<WRiFb)<khdd\6$0DlbW8Ws`M)At#"WfD-U="N">2enhi0[X,q9d?`&<X\(QoX2NTlr#9G;$nPD';ZcI#NLICT0@?EH3lt3MuD+=3kkCh)4r"0``M&iSnoBW;eKAm[8<@6%e\E0Qp%:o[[II^IGnTO7"p%[79h5C"/pigm,R(7/P&ERT:of$@@CRq[[D@hU>.4n1J>h`GJ=Lo*h*I3&2ihMjg(@KcXaLXU3H#b-PQr7p2_<UXt]#O3*TlL'YV!>o-Wm9CU+U_4_u(;%KJ=3-g(4dA"fV0B!`9,3/hor15,3CiV8JIf1i0T4mk!%[`qD0"InO*/do>;c/:l7?HFCeU/AJ)9D#b5-VH2aRIB(o.^"]mO@G<bTkW"DD9,UmEEBKnSFQB@^3P7lQ8#Zq>Gs=j_!Oe'gP*@;Hp*=7[a0U./f3K[[l*:#n#5@qYj#g_$'qR$oup/G&8QE)&Nfb`QYI=Xtk-4dXZXRauV7\gcjjqb4\FFbX?^<db6"LG[+V.V*o[p_aZGi>_mK],o$Jfae<Ko1>ShZ;Be3U4KaMK';l-CG-SZr2Yo:L21A0McHZ_#&t>[Qd$dd(DbZ2b6)'"Sg6XQ^^KgSb!!2q*-/b*kC<JLSN[cEdK$P1M,8,T8=^YKE9.5YpA@DL-[=*6rU38g_k^EKSZ`dB(PN+SlPM,3$QTO?rO.03Z5>[(18b0P8D8NIk)T+8](kt=^8r_)(SU=2Y<-1Vj1p'2b-QR:o2!+GeLZftCKMF%'iTS32i#U0D*B>"lH[>!WfY/r*U@Z@S%`O/H#ad6kh*@;PdM]2J^%VD6+gI8efA98j\J%.dppS>eedWR^ULf?7iAjr[,L2t78q84d0LbqtiUak6hZ#r2W"/~>endstream++endobj++xref++0 12++0000000000 65535 f++0000000061 00000 n++0000000112 00000 n++0000000219 00000 n++0000000331 00000 n++0000000446 00000 n++0000000640 00000 n++0000000834 00000 n++0000000902 00000 n++0000001275 00000 n++0000001340 00000 n++0000004521 00000 n++trailer++<<++/ID++[<a8f118002d6fd8b1daaf8b897f8fb0a6><a8f118002d6fd8b1daaf8b897f8fb0a6>]++% ReportLab generated PDF document -- digest (opensource)++++/Info 8 0 R++/Root 7 0 R++/Size 12++>>++startxref++6718++%%EOFnnew file mode 100644BBinary files /dev/null and b/public/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_Oct_2026_68186113.pdf and /dev/null differnnew file mode 100644BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_amber_38b37ff8.pdf differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_Themed_amber_68186113.pdf and /dev/null differnnew file mode 100644BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_cyan_38b37ff8.pdf differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_Themed_cyan_68186113.pdf and /dev/null differnnew file mode 100644BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_green_38b37ff8.pdf differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_Themed_green_68186113.pdf and /dev/null differnnew file mode 100644BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_indigo_38b37ff8.pdf differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_Themed_indigo_68186113.pdf and /dev/null differnnew file mode 100644BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_lime_38b37ff8.pdf differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_Themed_lime_68186113.pdf and /dev/null differnnew file mode 100644BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_orange_38b37ff8.pdf differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_Themed_orange_68186113.pdf and /dev/null differnnew file mode 100644BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_pink_38b37ff8.pdf differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_Themed_pink_68186113.pdf and /dev/null differnnew file mode 100644BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_red_38b37ff8.pdf differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_Themed_red_68186113.pdf and /dev/null differnnew file mode 100644BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_teal_38b37ff8.pdf differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_Themed_teal_68186113.pdf and /dev/null differnnew file mode 100644BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_violet_38b37ff8.pdf differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_Themed_violet_68186113.pdf and /dev/null differnnew file mode 100644BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_yellow_38b37ff8.pdf differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_Themed_yellow_68186113.pdf and /dev/null differ@@@ -1,14 +1,15 @@#!/usr/bin/env python3"""Build plain, one-column résumés for applicant tracking systems, from the same content as the site and the designed--PDF (build_resume.py), in two versions aimed at different roles:++PDF (build_resume.py), in versions aimed at different roles:public/Alex_Frison_Resume_Engineering.pdf software engineering (full-stack, payments, identity, streaming)public/Alex_Frison_Resume_Data_Analyst.pdf data / BI / operations analyst++ public/Alex_Frison_Resume_IT_Support.pdf IT, help desk and technical support (near-home jobs)The designed PDF is multi-column with icons; a tracking system reads its text layer column by column and scrambles it.These have one column, real text in reading order, standard fonts and plain section names, so a parser gets everyline right. Facts only come from src/data/profile.json and src/content/; what differs per version is the headline, the--summary, which skills lead and how many bullets each role keeps.++summary, which skills lead and which bullets each role keeps (a count, or the bullets' indexes).Run from the repo root: python3 resume/build_ats_resume.py"""@@@ -79,6 +80,32 @@ VERSIONS = {"founder": 2, "earlier": 1},"projects": ["irdr-autonomous-bot", "irdr-quality-recap", "amazon-userscripts"],},++ "support": {++ "file": "Alex_Frison_Resume_IT_Support.pdf",++ "headline": "IT and Technical Support: Linux and servers, hardware troubleshooting, customer support and escalations",++ "summary": (++ "Hands-on technical generalist in Arlington, WA. Ten years running my own company's servers: self-built "++ "and colocated 1U and 2U hardware, Linux, nginx and DDoS resilience, and later a full infrastructure "++ "migration with zero downtime and zero data loss. At Amazon Robotics I investigated recurring pod, drive-unit "++ "and workstation defects and escalated them to RME; as an ICQA data analyst I built tools other associates "++ "used every day. Three years at Verizon Wireless on phones, plans and accessories, promoted to Assistant "++ "Manager running inventory and escalations. I document what I fix: incident write-ups, runbooks and "++ "support docs."),++ "skills": [++ ("Support and troubleshooting", ["hardware and electro-mechanical troubleshooting", "root-cause analysis",++ "incident documentation", "escalations", "support docs and runbooks"]),++ ("Systems", ["Linux", "server hardware (self-built and colocated 1U / 2U)", "nginx", "systemd", "Docker",++ "Cloudflare", "cloud storage (Backblaze B2 / R2 / S3 APIs)", "zero-downtime migrations"]),++ ("Network and web", ["HAR network-trace debugging (HARTools)", "DDoS resilience", "WebSockets", "TURN / coturn"]),++ ("Scripting and automation", ["Python", "JavaScript / TypeScript", "SQL", "PHP", "TamperMonkey userscripts",++ "Excel / Power Query / VBA"]),++ ("People", ["customer retention and consultative sales", "coaching a team of 4 to 8", "opens, closes and inventory",++ "technical writing"]),++ ],++ "bullets": {"PowerChat": 1, "OpenVibe": [0, 2], "Amazon/analytics": [0, 2], "Amazon/operations": 2,++ "Verizon Wireless": 3, "founder": 2},++ "projects": ["amazon-userscripts", "hartools", "openvibe-cutover", "mp-select-mini"],++ },}@@@ -105,12 +132,14 @@ def plain(s: str) -> str:.replace("‘", "'").replace("’", "'").replace("“", '"').replace("”", '"'))--def bullets_for(role: dict, counts: dict) -> int:++def bullets_for(role: dict, counts: dict) -> list[str]:key = role["company"] if role["company"] in counts else (f"Amazon/{role.get('kind')}" if role["company"] == "Amazon" else role.get("kind", ""))if role["company"].startswith("OpenVibe"):key = "OpenVibe"-- return counts.get(key, counts.get(role.get("kind", ""), 2))++ pick = counts.get(key, counts.get(role.get("kind", ""), 2))++ bullets = role.get("bullets", [])++ return [bullets[i] for i in pick if i < len(bullets)] if isinstance(pick, list) else bullets[:pick]def build(version: str, data: dict) -> Path:@@@ -143,11 +172,11 @@ def build(version: str, data: dict) -> Path:story.append(Paragraph(f"<b>{plain(group)}:</b> {plain(', '.join(items))}", st["body"]))story += [Paragraph("EXPERIENCE", st["h"]), rule()]for r in data["roles"]:-- n = bullets_for(r, v["bullets"])++ chosen = bullets_for(r, v["bullets"])head = Paragraph(plain(f"{r['role']}, {r['company']}"), st["role"])meta = Paragraph(plain(" | ".join(x for x in [r.get("location", ""), f"{r['start']} to {r['end']}"] if x)), st["meta"])-- items = [Paragraph(plain(b), st["bullet"], bulletText="•") for b in r.get("bullets", [])[:n]]-- if r.get("stack") and version == "engineering" and n >= 2:++ items = [Paragraph(plain(b), st["bullet"], bulletText="•") for b in chosen]++ if r.get("stack") and version == "engineering" and len(chosen) >= 2:items.append(Paragraph(plain("Stack: " + ", ".join(r["stack"])), st["meta"]))story.append(KeepTogether([head, meta, *items[:1]]))story += items[1:]@@@ -1280,8 +1280,9 @@ def main() -> None:json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8")# the one-column versions for tracking systems (build_ats_resume.py) live next to these: never swept++ from build_ats_resume import VERSIONS as ATS_VERSIONSkeep |= {p for p in (ROOT / "public").glob("Alex_Frison_Resume_*.pdf")-- if p.name in ("Alex_Frison_Resume_Engineering.pdf", "Alex_Frison_Resume_Data_Analyst.pdf")}++ if p.name in {v["file"] for v in ATS_VERSIONS.values()}}clean_old_files("Alex_Frison_Resume_*.pdf", keep)print(f"\ncontent hash: {h} (filenames only change when the résumé content does)")print(f"text font: {FONT['label']}; icons: Font Awesome 4")@@@ -1,6 +1,11 @@---import profile from "../data/profile.json";--const sites = profile.network;++// The "sites" tile stands for everything past the six core services (profile.networkLive); its++// text carries {more} and {total} so the counts come from that list and never drift.++const more = profile.networkLive.length;++const total = profile.network.filter((s) => s.key !== "sites").length + more;++const fill = (t: string) => t.replace("{more}", String(more)).replace("{total}", String(total));++const sites = profile.network.map((s) => ({ ...s, name: fill(s.name), blurb: fill(s.blurb), stat: fill(s.stat) }));const big = sites[0];const rest = sites.slice(1);const icons: Record<string, string> = {@@@ -17,7 +22,7 @@ const nodes = [{ key: "tools", label: "Tools", x: 400, y: 50 },{ key: "games", label: "Games", x: 400, y: 372 },{ key: "community", label: "Community", x: 670, y: 90 },-- { key: "sites", label: "13 Sites", x: 670, y: 330 },++ { key: "sites", label: `${more} more`, x: 670, y: 330 },];const edges = [["live", "media"],@@@ -61,7 +66,7 @@ const edges = [<div class="arch card" data-reveal><div class="arch__copy"><div class="eyebrow"><i class="fa-solid fa-diagram-project" aria-hidden="true"></i> How it fits together</div>-- <h3>One login, one wallet, seven products that feel like one.</h3>++ <h3>One login, one wallet, {total} sites that feel like one.</h3><details class="arch__more" open><summary><span class="arch__more-closed">Read more</span><span class="arch__more-open">Show less</span></summary><p>Sign in once and every OpenVibe site knows who you are, what you own and what you look like. The identity service in the middle handles accounts, security and the shared virtual currency; every other product plugs into it through clear, documented APIs. That's the same playbook I bring to any company: secure single sign-on, payments and APIs designed so new products can be added without breaking the old ones.</p>@@@ -73,7 +78,7 @@ const edges = [<li><i class="fa-solid fa-shield-halved"></i> Every migration is rehearsed as a dry run before it touches real users</li></ul></div>-- <svg class="arch__svg" viewBox="0 0 800 420" role="img" aria-label="Diagram: OpenVibe.Network at the center connected to Live, Media, Tools, Games, Community and 13 static sites; Live also talks to Media.">++ <svg class="arch__svg" viewBox="0 0 800 420" role="img" aria-label={`Diagram: OpenVibe.Network at the center connected to Live, Media, Tools, Games, Community and ${more} more sites; Live also talks to Media.`}><defs><linearGradient id="agrad" x1="0" y1="0" x2="1" y2="0"><stop offset="0" stop-color="var(--accent)"/><stop offset="1" stop-color="var(--accent-2)"/></linearGradient><filter id="aglow" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="6" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>@@@ -23,11 +23,19 @@ const netIcon: Record<string, string> = {games: "fa-solid fa-gamepad", community: "fa-solid fa-comments", sites: "fa-solid fa-globe",};++// Everything past the six core services: live sites, shown as chips under the bento.++const liveMore = profile.networkLive;++const liveTotal = profile.network.filter((s) => s.key !== "sites").length + liveMore.length;++const liveGroups = [++ { label: "Also live", icon: "fa-solid fa-circle-check", items: liveMore.filter((n) => n.group === "people") },++ { label: "For builders", icon: "fa-solid fa-code", items: liveMore.filter((n) => n.group === "builders") },++];++const groups = [{key: "openvibe", tone: "a", anchor: "network", icon: "fa-solid fa-diagram-project",-- title: "The OpenVibe network", blurb: "Seven live services behind one sign-in, all built and run by me since 2026.",-- count: profile.network.length, allHref: "/portfolio/?cat=openvibe,infra", allLabel: "All OpenVibe case notes",++ title: "The OpenVibe network", blurb: `${liveTotal} live sites behind one sign-in, all built and run by me since 2026.`,++ count: liveTotal, allHref: "/portfolio/?cat=openvibe,infra", allLabel: "All OpenVibe case notes",items: [] as { href: string; external: boolean; image?: string | null; icon: string; title: string; sub: string; text: string; meta: string; soon?: boolean }[],},{@@@ -73,16 +81,16 @@ const groups = [<a class="wk-group__all" href={g.allHref}><i class="fa-solid fa-grip" aria-hidden="true"></i> <span>{g.allLabel}</span> <i class="fa-solid fa-arrow-right wk-group__arrow" aria-hidden="true"></i></a></header>{g.key === "openvibe" && <OpenVibeBento />}-- {g.key === "openvibe" && (-- <div class="wk-planned" aria-label="Planned OpenVibe sites">-- <span class="wk-planned__label"><i class="fa-solid fa-clock" aria-hidden="true"></i> Coming soon</span>++ {g.key === "openvibe" && liveGroups.map((lg) => (++ <div class="wk-planned" aria-label={`${lg.label}: more OpenVibe sites`}>++ <span class="wk-planned__label"><i class={lg.icon} aria-hidden="true"></i> {lg.label}</span><ul class="wk-planned__list">-- {profile.networkPlanned.map((n) => (-- <li><a href={n.url} target="_blank" rel="noopener" title={`${n.name}: ${n.role} (placeholder page)`}>{n.name.replace("OpenVibe.", ".")}<small>{n.role}</small></a></li>++ {lg.items.map((n) => (++ <li><a href={n.url} target="_blank" rel="noopener" title={`${n.name}: ${n.role}`}>{n.name.replace("OpenVibe.", ".")}<small>{n.role}</small></a></li>))}</ul></div>-- )}++ ))}<ul class="wk-grid" data-stagger hidden={g.items.length === 0} style={`--cols:${Math.min(Math.max(g.items.length, 1), 4)};--cols-t:${Math.min(Math.max(g.items.length, 1), 2)}`}>{g.items.map((it) => (<li class={`wk-card ${it.image ? "wk-card--img" : ""} ${it.soon ? "wk-card--soon" : ""}`}>@@@ -167,8 +175,9 @@ const groups = [.mono { font-family: var(--font-mono); }.wk-card__soon { margin-left: auto; display: inline-flex; align-items: center; gap: 5px; font-size: .6rem; letter-spacing: .08em; text-transform: uppercase; padding: 2px 8px; border-radius: 999px; color: var(--accent-3); background: rgba(var(--accent-3-rgb), .12); border: 1px solid rgba(var(--accent-3-rgb), .35); white-space: nowrap; }.wk-card--soon .wk-card__link { border-style: dashed; }-- /* Planned domains: every one already has a truthful "coming soon" page, so the chips link there. */-- .wk-planned { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-top: 14px; padding: 12px 14px; border-radius: 14px; border: 1px dashed var(--border-strong); background: var(--surface); }++ /* The rest of the live network, as chips that open each site (2026-10: every one of them serves its product). */++ .wk-planned { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-top: 14px; padding: 12px 14px; border-radius: 14px; border: 1px solid var(--border); background: var(--surface); }++ .wk-planned + .wk-planned { margin-top: 8px; }.wk-planned__label { display: inline-flex; align-items: center; gap: 7px; font: 600 .68rem var(--font-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--accent-3); }.wk-planned__list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }.wk-planned__list a { display: inline-flex; align-items: baseline; gap: 6px; padding: 5px 10px; border-radius: 999px; border: 1px solid var(--border); background: var(--surface-2); color: var(--fg); font: 600 .74rem var(--font-body); text-decoration: none; transition: border-color .25s var(--ease), transform .25s var(--ease); }@@@ -7,7 +7,7 @@"order": 6,"bullets": ["Built analytics platforms tracking 200,000+ live game servers: ingestion, search, ranking, dashboards and licensed partner APIs. Ranked first on Google for several major titles.",-- "Self-built and collocated 1U and 2U servers in datacenters; designed for scale and DDoS resilience and ran the whole LLC, from licenses and taxes to partner relations.",++ "Self-built and colocated 1U and 2U servers in datacenters; designed for scale and DDoS resilience and ran the whole LLC, from licenses and taxes to partner relations.","Rebuilding the idea in 2026 as GameServerLists (launching soon): a 14-service, game-agnostic server-discovery platform with multi-region probes, time-weighted ranking, an anti-fraud integrity engine and a tiered public API."],"stack": [@@@ -36,11 +36,11 @@"githubDisplay": "github.com/OpenVibers","linkedin": "https://www.linkedin.com/in/fris/","linkedinDisplay": "linkedin.com/in/fris",-- "resumePdf": "/Alex_Frison_Resume_Oct_2026_68186113.pdf",++ "resumePdf": "/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf","availability": "Open to pretty much anything — engineering, platform, analytics, ops, sales, whatever fits · remote or Pacific Northwest","status": {"now": "Contract software engineer at PowerChat, building its developer platform and payments program.",-- "also": "Founder and sole engineer of OpenVibe: seven services, 20+ domains, one operator."++ "also": "Founder and sole engineer of OpenVibe: 39 live sites behind one login, one operator."},"pitch": "I'm a software engineer and business owner who builds complete products and keeps them running: websites, platforms, payment systems, AI agents and the automation behind them. Over the last decade I've shipped 25+ sites and services, rebuilt a live payments program across five processors, led a sales team, and turned an Amazon data-analysis job into tooling other Amazon associates used every day. Give me a problem or a rough idea and I'll deliver the whole thing: design, code, deployment and support.","seeking": [@@@ -68,18 +68,42 @@{ "key": "tools", "name": "OpenVibe.Tools", "url": "https://openvibe.tools", "role": "Tools suite", "blurb": "169 server-rendered tools across 8 families, a host registry with custom-domain mirrors, and full SEO plumbing.", "stat": "169 tools · 8 families", "image": "/assets/img/sites/tools.webp" },{ "key": "games", "name": "OpenVibe.Games", "url": "https://openvibe.games", "role": "Scraplandia", "blurb": "Browser multiplayer survival game: authoritative server, client prediction, Havok physics, physgun building.", "stat": "56k LOC TypeScript", "image": "/assets/img/sites/games.webp" },{ "key": "community", "name": "OpenVibe.Community", "url": "https://openvibe.community", "role": "Pastes & posts", "blurb": "Server-rendered paste hub with forks, search, feeds and silent SSO, proxied to Live so identity stays consistent.", "stat": "No DB of its own", "image": "/assets/img/sites/community.webp" },-- { "key": "sites", "name": "OpenVibe.Sites", "url": "https://openvibe.chat", "role": "13 front doors", "blurb": "One catalog and one template generate the pages and nginx configs for openvibe.chat, .codes, .wiki, .news and more.", "stat": "13 domains · 1 build", "image": null }++ { "key": "sites", "name": "{more} more live sites", "url": "https://openvibe.network", "role": "The rest of the network", "blurb": "Chat, tips, memberships, video, blogs, wikis, cited news, restreaming, job and home search, robot control, coding agents and a developer platform, all on the same login.", "stat": "{total} live sites · 1 login", "image": null }],-- "networkPlanned": [-- { "name": "OpenRestream", "url": "https://openre.stream", "role": "Restreaming" },-- { "name": "OpenVibe.Tips", "url": "https://openvibe.tips", "role": "Creator tips" },-- { "name": "OpenVibe.VIP", "url": "https://openvibe.vip", "role": "Memberships" },-- { "name": "OpenVibe.Host", "url": "https://openvibe.host", "role": "Hosting" },-- { "name": "OpenVibe.News", "url": "https://openvibe.news", "role": "News" },-- { "name": "OpenVibe.Reviews", "url": "https://openvibe.reviews", "role": "Reviews" },-- { "name": "OpenVibe.Deals", "url": "https://openvibe.deals", "role": "Deals" },-- { "name": "OpenVibe.Coupons", "url": "https://openvibe.coupons", "role": "Coupons" },-- { "name": "OpenVibe.Trade", "url": "https://openvibe.trade", "role": "Markets" }++ "networkLive": [++ { "name": "OpenRestream", "url": "https://openre.stream", "role": "Restreaming", "group": "people" },++ { "name": "OpenVibe.Chat", "url": "https://openvibe.chat", "role": "Global chat", "group": "people" },++ { "name": "OpenVibe.Tips", "url": "https://openvibe.tips", "role": "Creator tips", "group": "people" },++ { "name": "OpenVibe.VIP", "url": "https://openvibe.vip", "role": "Memberships", "group": "people" },++ { "name": "OpenVibe.Video", "url": "https://openvibe.video", "role": "Video", "group": "people" },++ { "name": "OpenVibe.Pics", "url": "https://openvibe.pics", "role": "Pictures", "group": "people" },++ { "name": "OpenVibe.Download", "url": "https://openvibe.download", "role": "File sharing", "group": "people" },++ { "name": "OpenVibe.Blog", "url": "https://openvibe.blog", "role": "Blogs", "group": "people" },++ { "name": "OpenVibe.Wiki", "url": "https://openvibe.wiki", "role": "Wikis", "group": "people" },++ { "name": "OpenVibe.News", "url": "https://openvibe.news", "role": "Cited news", "group": "people" },++ { "name": "OpenVibe.Reviews", "url": "https://openvibe.reviews", "role": "Sourced reviews", "group": "people" },++ { "name": "OpenVibe.Help", "url": "https://openvibe.help", "role": "Help and support", "group": "people" },++ { "name": "OpenVibe.Quest", "url": "https://openvibe.quest", "role": "Quests", "group": "people" },++ { "name": "OpenVibe.Play", "url": "https://play.openvibe.network", "role": "Achievements", "group": "people" },++ { "name": "OpenVibe.Inventory", "url": "https://inventory.openvibe.network", "role": "Items", "group": "people" },++ { "name": "OpenVibe.Deals", "url": "https://openvibe.deals", "role": "Deals", "group": "people" },++ { "name": "OpenVibe.Coupons", "url": "https://openvibe.coupons", "role": "Coupons", "group": "people" },++ { "name": "OpenVibe.Trade", "url": "https://openvibe.trade", "role": "Market data", "group": "people" },++ { "name": "OpenVibe.Work", "url": "https://openvibe.work", "role": "Job search", "group": "people" },++ { "name": "OpenVibe.Rent", "url": "https://openvibe.rent", "role": "Rentals", "group": "people" },++ { "name": "OpenVibe.Homes", "url": "https://openvibe.homes", "role": "Home buying", "group": "people" },++ { "name": "OpenVibe.Food", "url": "https://openvibe.food", "role": "Food and meals", "group": "people" },++ { "name": "OpenVibe.Host", "url": "https://openvibe.host", "role": "Hosting compared", "group": "people" },++ { "name": "OpenVibe.Services", "url": "https://openvibe.services", "role": "Developer platform", "group": "builders" },++ { "name": "OpenVibe.AI", "url": "https://ai.openvibe.services", "role": "AI layer", "group": "builders" },++ { "name": "OpenVibe.Codes", "url": "https://openvibe.codes", "role": "Coding agents", "group": "builders" },++ { "name": "OpenVibe.Actor", "url": "https://openvibe.actor", "role": "Task agents", "group": "builders" },++ { "name": "OpenVibe.Bot", "url": "https://openvibe.bot", "role": "Robot control", "group": "builders" },++ { "name": "OpenVibe.Run", "url": "https://openvibe.run", "role": "Jobs on nodes", "group": "builders" },++ { "name": "OpenVibe.Events", "url": "https://openvibe.events", "role": "Event delivery", "group": "builders" },++ { "name": "OpenVibe.Watch", "url": "https://openvibe.watch", "role": "Watches", "group": "builders" },++ { "name": "OpenVibe.Space", "url": "https://openvibe.space", "role": "Static hosting", "group": "builders" },++ { "name": "OpenVibe.Zone", "url": "https://openvibe.zone", "role": "S3 storage", "group": "builders" }],"education": "Snohomish High School, WA. AP Statistics, Calculus, English, Creative Writing and Spanish. Self-taught engineer since age 12: systems, infrastructure, graphics, hardware and whatever the problem needs.","capabilities": ["websites", "platforms", "APIs", "dashboards", "AI features", "automations", "identity systems", "payment systems", "real-time chat", "streaming infra", "whatever you need"]@@@ -1,15 +1,15 @@{-- "amber": "/Alex_Frison_Resume_Themed_amber_68186113.pdf",-- "blue": "/Alex_Frison_Resume_Oct_2026_68186113.pdf",-- "cyan": "/Alex_Frison_Resume_Themed_cyan_68186113.pdf",-- "default": "/Alex_Frison_Resume_Oct_2026_68186113.pdf",-- "green": "/Alex_Frison_Resume_Themed_green_68186113.pdf",-- "indigo": "/Alex_Frison_Resume_Themed_indigo_68186113.pdf",-- "lime": "/Alex_Frison_Resume_Themed_lime_68186113.pdf",-- "orange": "/Alex_Frison_Resume_Themed_orange_68186113.pdf",-- "pink": "/Alex_Frison_Resume_Themed_pink_68186113.pdf",-- "red": "/Alex_Frison_Resume_Themed_red_68186113.pdf",-- "teal": "/Alex_Frison_Resume_Themed_teal_68186113.pdf",-- "violet": "/Alex_Frison_Resume_Themed_violet_68186113.pdf",-- "yellow": "/Alex_Frison_Resume_Themed_yellow_68186113.pdf"++ "amber": "/Alex_Frison_Resume_Themed_amber_38b37ff8.pdf",++ "blue": "/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf",++ "cyan": "/Alex_Frison_Resume_Themed_cyan_38b37ff8.pdf",++ "default": "/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf",++ "green": "/Alex_Frison_Resume_Themed_green_38b37ff8.pdf",++ "indigo": "/Alex_Frison_Resume_Themed_indigo_38b37ff8.pdf",++ "lime": "/Alex_Frison_Resume_Themed_lime_38b37ff8.pdf",++ "orange": "/Alex_Frison_Resume_Themed_orange_38b37ff8.pdf",++ "pink": "/Alex_Frison_Resume_Themed_pink_38b37ff8.pdf",++ "red": "/Alex_Frison_Resume_Themed_red_38b37ff8.pdf",++ "teal": "/Alex_Frison_Resume_Themed_teal_38b37ff8.pdf",++ "violet": "/Alex_Frison_Resume_Themed_violet_38b37ff8.pdf",++ "yellow": "/Alex_Frison_Resume_Themed_yellow_38b37ff8.pdf"}
@@ -47,7 +47,7 @@ endobj
@@ -47,7 +47,7 @@ endobj
endobj
endobj
8 0 obj
8 0 obj
<<
<<
-/Author (Alex Frison) /CreationDate (D:20261020184315-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261020184315-07'00') /Producer (ReportLab PDF Library - \(opensource\))
+/Author (Alex Frison) /CreationDate (D:20261021063644-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261021063644-07'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (Data Analyst: SQL, Excel and Power Query, compliance reporting and automation) /Title (Alex Frison - Resume) /Trapped /False
/Subject (Data Analyst: SQL, Excel and Power Query, compliance reporting and automation) /Title (Alex Frison - Resume) /Trapped /False
>>
>>
endobj
endobj
@@ -65,10 +65,10 @@ Gb!#]>>s9I(4OT53,_0-D9%fXR!i+YaUYY=&7B-`X7F])g3+!i>3Tp54>f?P^XFdXL&_\uC+:B<**rEr
@@ -65,10 +65,10 @@ Gb!#]>>s9I(4OT53,_0-D9%fXR!i+YaUYY=&7B-`X7F])g3+!i>3Tp54>f?P^XFdXL&_\uC+:B<**rEr
endobj
endobj
11 0 obj
11 0 obj
<<
<<
-/Filter [ /ASCII85Decode /FlateDecode ] /Length 1880
+/Filter [ /ASCII85Decode /FlateDecode ] /Length 1879
>>
>>
stream
stream
-Gb!#\?#poC'Rf_Z\506]!b(m[,Yo9;e/:>k!7uY_MnCQB's4j*ZmK>Sj*RT`_b&XbBBmcM4L"1TY-dBKF7'VP2=ZR@H^k\=cKNf(6rAkSF,E#<%a?V?4?NCa'Q*:)p2KSuh/'GP!9Y<G*r\pugJ>.86Ek,>^X_Ps/49%F%K%Bd[i.COrci+YY`:/&=%ok&2tW!)9QEc>Sf;RXEm0@F?DB`p&YHJ*4(1+OgL"Xf2pHgE/,XqL\#YJaR%[brCU:Tj5/1H9Y?YY.#%<M=ArWp)X%?&"52/;n[5[;cZ$@%p78^N+Q*GmYZNS(!rc\A,poioB,g;"BSd0B0ogFF%HNSDXlZg:rOse1'XVTtq:'m"@,'H1T4C(]B2figkDJY;AXaFH*^0D?O'Q]iON5ADXiloG04(a>g7U&N2WYNRI>V]l+>D/#>*$\23LiCUPa($KY5c,0_$cQ)(OGsT4HbNkt'[Reo1H\[sCZE#fIKL!]S9humEL)`Y;odeK-c:!JSf1lc;MsaCeKZ1nQWfl!OGDPbA=#VT>%t=MWQlSQMi3<=dQ(Am>>eZ9Z;;(QQo=7aRbO3$jrq36.!JlK8Ek2$W!#Nf6>-4I9<?M_:Y."mbgJ2eKW<Gm9&WpZS>gXj/h]^0$g]]WONgYSkM`:;Q\pWphVf+DZk1Y/eP*-:Looj4o*e*.)&=lR't'LT>-lo[N?g?F(+eKV\Tn1q?_^d?h"pT?i>aXn>4XW5LXde]2SI_d,W:mM'JTrS-bBH(R;iRZ*St&R#&TT9/rXoUL;28b+ZWs_LlBHCUl>83Db`8_G==B4#LVLNI`gcbI_PnI5m^2`A*-3&\d`:U[p+Ld0Q;+BcoHK%6OC52Tfc-m(m'r:&SGa&$;I`k$EiCZS0te4kZ+W/hk:OO5UW3cgOtgEEU4A5&l.2_TKtc"pYD%2g=;5=>=Lh$q2_a$1P7&/1uiSU"<NVQU=6sdoEIs4&@%Z?">(^G^__<DV3<@8MW:(rZ?=,YH``VKJ6(@KUMe&PmJ`fc5:`NC+T<:pQh2nQ[cVooGN(Eo(9/E.EHH>oM+uMG@Nc_SK[-)%Z*RuK9Qk8e)B4gNF.IP%a6gJWguYZ<[\EKSZM5b/1S@kfQD`))877SCg,M^3s2OA<:9['XUI8IE-C1<9)L)C&Kslr3]u&U;G,ushh=n=Y:r3C3G7E[o0YRUrc<gC>hheu":)l.i0qP^WC=W*"4IV8Uk[nl6%S,!*\bh#M$g>tF(Z5%@npg6(8;3NhXO5qW,Y?H4_t<pMq0kB:gQ\!BeZR?!@Gs<RFCFECE]0VJeuh2GL#mEVb>^X)QfH]iBE29Xm%$e;gGiIM%N5eMk78d,dTonjap<dHHZp^FgT1)@qTjII.D/C1HSt7ZI/ETN&/C#2W@e_sL,&=D(e[gSr":j0H4D<&]&%.ni;Go>&9.YG0IbcqS%P-MBFFA$$1-Pjk=J]]!F9M%aq5tZ82,WJL_VOr'I=\ao@o]1_DgLWUB>iF"OLijHG%Qm`r40f:`EaAE@:g"32!BjqRSD1])pI"b$@Y2Gno`ihLVCbba@9Mhnkl8+RM!;`25N/7XZaMd>VXUm8k47DGA)fN%M*fn-ng'gB_U!kC*[#!=HP/GcQIkV2N`V@6ejXI^MQ!Be;u)aa"A)<D1-pfKG0)=,<&PTL=e-$b>'.?A3WN&A=<3NmDT5_qE$%qhc9gr@PMXMb:F)q]3p;Q]B:6p'n.1BkX&8lBP,lg20HsMpT.95[C.s@07:9VZ&LglU&FKR#DDUi+T2-k\Y;;,K%E;/9D-Yg?q,%fUCc6K7`-+%&Uf`9+*R93!-V$7r3c4@O\MThF"6P@:1=-7:@<sq_jpQ:U+B+FL`o$PM[GbWs^#$6HBH&^DAjjq?LK6m(<~>endstream
+Gb!#\?#poC'Rf_Z\506]!b(m[,Yo9;e/:>k!7uY_MnCQB's4j*ZmK>Sj*RT`_b&XbBBmcM4L"1TY-dBKF7'VP2=ZR@H^k\=cKNf(6rAkSF,E#<%a?V?4?NCa'Q*:)p2KSuh/'GP!9Y<G*r\pugJ>.86Ek,>^X_Ps/49%F%K%Bd[i.COrci+YY`:/&=%ok&2tW!)9QEc>Sf;RXEm0@F?DB`p&YHJ*4(1+OgL"Xf2pHgE/,XqL\#YJaR%[brCU:Tj5/1H9Y?YY.#%<M=ArWp)X%?&"52/;n[5[;cZ$@%p78^N+Q*GmYZNS(!rc\A,poioB,g;"BSd0B0ogFF%HNSDXlZg:rOse1'XVTtq:'m"@,'H1T4C(]B2figkDJY;AXaFH*^0D?O'Q]iON5ADXiloG04(a>g7U&N2WYNRI>V]l+>D/#>*$\23LiCUPa($KY5c,0_$cQ)(OGsT4HbNkt'[Reo1H\[sCZE#fIKL!]S9humEL)`Y;odeK-c:!JSf1lc;MsaCeKZ1nQWfl!OGDPbA=#VT>%t=MWQlSQMi3<=dQ(Am>>eZ9Z;;(QQo=7aRbO3$jrq36.!JlK8Ek2$W!#Nf6>-4I9<?M_:Y."mbgJ2eKW<Gm9&WpZS>gXj/h]^0$g]]WONgYSkM`:;Q\pWphVf+DZk1Y/eP*-:Looj4o*e*.)&=lR't'LT>-lo[N?g?F(+eKV\Tn1q?_^d?h"pT?i>aXn>4XW5LXde]2SI_d,W:mM'JTrS-bBH(R;iRZ*St&R#&TT9/rXoUL;28b+ZWs_LlBHCUl>83Db`8_G==B4#LVLNI`gcbI_PnI5m^1)1Tpi,D)fqSh#\"4!Ma-icJe(.9"5TDj`0k$J=AZ[3'5Sf8:hRSE#"`&4L`!jY>QtJ%Wp',eUk+bV1A"U84E7uWBrUF$tn")YlX`iLV')E3\Ig#G3)*EJD)37]Bn9FR=SJG8!2-@GBL=NpXB__W2^<kUbSP12,*67uAXXdH+CV;"n>@faS\eVQ[qhIrGXH62-pM0b9ZFc@SRW$a3M9c0+W-YtldF<:c2H>rsKlBQq)KSfpcB?^5fla-*Cdd^:FlJ)p\*n'NQaa!KMV<hYig]G]J7`4lSjGK3J46&Fj!`=S)m?"q_$P[Q&oAYP=FuiQi"<I6\h[I_$?tsMZ')&/hq#)"#oA][op=^Sp^5M3Hg,m+,OTFgERaX$Gg:I]<TD9mBJr&LS&t=b*).sGmLOIfTLUM;I-2$_s2j"?'lM=$7Xnd/,Yam>e,+XEqs!Hb;d*p8]1SiQL%?;2Kr'^Lfn:\U]D@<uQIDfc;t[(qNaL81!AJY8G:mHE@rt!rBJbC65D[6NG?G5&,9/K-c&Le%YMRao5O7:(n1J^!e[u@]h:LCJ<$:1t\j06i`R*>t.oDS99@,6U3Lj(5HDc1CpB+,U_#6X:Ui:,9nR@fZ/]>g[Y^aui-Wtj$FKsjS8!ktS_$IUp<k&iAP\^KImq1tqhi)')%da-)5c63tnNIa,pDY2>D0uB&T`CVAJ@;n#lc/_U$9StP2_?37<Od[+#E&@3@t*lo$ZBL,[*uAnS)X>aor7^S1R>ka?YR$]2&3_X[]Fqq1VaB:,HqiOq[K>+./4G6XU^5S/k-q;!Qj<reIH`"[4L,[k;e2q/HN=jpFnp=+\nJW&iI6RjDk?L(#nUHoMTEa5._gEB,B1g.q\/B\(pg5.bX7W2`/ic3\oUG2mgl-Tliu,=#rcc[jh8.6MF4UFcGgD%6/HuqO^Tc$O&b(1+m*gUTn!q\&%]t$kGS-clJ?d]Ch\'B83Gr$AR.eQi`$o/^WG6#iOthYmJR/V7bZ3&#&-?JW9.6Kn1fCgMDa!`JU)3jfVESIX;X-/pmp3+X=r*?Jt&2+1';[:]~>endstream
endobj
endobj
xref
xref
0 12
0 12
@@ -87,7 +87,7 @@ xref
@@ -87,7 +87,7 @@ xref
trailer
trailer
<<
<<
/ID
/ID
-[<4db2b9d6052cdbd87e4d08758dd69ff6><4db2b9d6052cdbd87e4d08758dd69ff6>]
+[<0708869bd847a2f084ce65d9fc9de7c3><0708869bd847a2f084ce65d9fc9de7c3>]
% ReportLab generated PDF document -- digest (opensource)
% ReportLab generated PDF document -- digest (opensource)
/Info 8 0 R
/Info 8 0 R
@@ -95,5 +95,5 @@ trailer
@@ -95,5 +95,5 @@ trailer
/Size 12
/Size 12
>>
>>
startxref
startxref
-6393
+6392
%%EOF
%%EOF
@@ -47,7 +47,7 @@ endobj
@@ -47,7 +47,7 @@ endobj
endobj
endobj
8 0 obj
8 0 obj
<<
<<
-/Author (Alex Frison) /CreationDate (D:20261020184315-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261020184315-07'00') /Producer (ReportLab PDF Library - \(opensource\))
+/Author (Alex Frison) /CreationDate (D:20261021063644-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261021063644-07'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (Software Engineer: full-stack TypeScript and Node.js, payments, identity and live streaming) /Title (Alex Frison - Resume) /Trapped /False
/Subject (Software Engineer: full-stack TypeScript and Node.js, payments, identity and live streaming) /Title (Alex Frison - Resume) /Trapped /False
>>
>>
endobj
endobj
@@ -68,7 +68,7 @@ endobj
@@ -68,7 +68,7 @@ endobj
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2981
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2981
>>
>>
stream
stream
-Gb!#]flGh,(4FM1gnu%aN229Zih`8Xfh%]S@RX2SI`Mmb66b,/_#U->FnNf$\KE3!e*\oh;5tV6CbF@bQZLFU!p\bmn@t`Wk^6,Y?Nh-s1ga/-n+:=C9pbGf?X<kXbJ=`#O+Yp,&0:8Q'+XR$R(iQ8<\H./YT"[:@F,G#pZU+f*af84IPc*i\iZ\]3gQ>oD2[PG6I$dM$J2#qLVcT?LU.NCb";GWF?@)5r(Dr')(T-<=gVioNEW(DEg[Y_L&49k6CLYKi8qLOCf6i$J_u91IFk3_;<a7*?N*:_heE'33kDl5(q40>$O9%FHTQ.C^/FboMsSGERDg`rVn.hRWA"qeU*i[Q(/9b5Ue>64gX^4>[email protected]>lZL,!G2:ka?iBrWG%'R010mGKHjShe[IGl&'/[j#MRBnVCA_MiU/<9:d.t0&0Kea^pCDPr:\g4W-_nkHrhPZJ<PEK7DpdqXpL-#IPLK/!(qq%L/8/,jeD?]="bTQH9o>V1<aX-nrj1_Mk]:]jm,u5Rhe1p+Y7mIMHF8b2>Xpa=>fHnJe*Q5+G,2Gg<?dJL>tluL[+=AWRHjG_+m?"^/(+kMBVdC[Jpp"dg[5!,<.6KAVp,Ec@O!**N1csmOiOgW<U0Jb1#0<tb:<Lk-Kq+^K72-@33pCM]2TVr-Hng;d^F'8;RmrJ`lb"JQVn]h@7K!cY^8Uu4Bp"$Hc%!#]aZS9,+7k%@]@VD=[a$i7RS3Q1mUcP3P&9m/QAb*k#XZ;e\*dn=Y+Bpd)*`:"=)=;n7\d80cZRZ@cr8VWlb?oEp;6.mBqftI45=1bPak\Z(noX486;k9",]l@?jl1P*\#Q5XG6C8f6P6I6%T?9K"N^>TDiX0tL0nN@%^l=09qpBdJTN7gC(@^nuCTo/jKsEJ2eNKK"8kr7!qJP)V4b0eE/NeaP*?UYs]TR*ekd1aD=1U0G*bd19aTmECfP163'RbX3H-HAla#Eo13Ii-;ahe+:beg<MRiI:[4uH=+U%Pp"WOCIAQW8*BT)A<%XgcrnlD6Z38Q77\aH;_P4.A`R':(@<mC:)8is*EU?tOI5DJ$fMED!4Se@aJQf3AEVkQKV#To3fuLC<K=^GaJ#"mJ^.39q?)S+0>7\gItH6Z$f(kin\0S<#Q<[XGb?`YS9=.BlSZm)L0NCp^Nc4Ti!dYap/K&3#pH#epMYtn/!r[9;Ak!%;1tma>?Du+.Y2]gI-?9e0#<`)V02oK'cCj?'hK-d74N?4g'&Bs!H-=Tq0,:il(cYHh,<?._YL2Tf_VKncSpDXL>o7CNfCfmnZT0jn1$hfP9M6b,d5<bQptM(0LNBC-tqqZ*Pb!A_Y[)EC:9;=;ebMsN_OW#Dd&GQ,%j0^IElWO7c%Wc1lsU.Ba#;I<^%BB%'s`,*+RPTX`>5VP4J2+CkbH_LUaNP();@A6D]#/LR4i[OSK+Ci]'n3`7#g"Fq,nf?g0>d3l*]jnm83+0O2q==ZD9p"b)4foo(JCd.d5+\7'r@iubDY^&^gAJ:hQ66"ImO^#%<qcS(,)SLM(-o"52=E=>4U&4jr*:5,q,Xb8g;86UiY&L@kh-"mkY_1%9l<[IWIS/?H6W#ipTc\GIga;4TmKS;*4LL24MCklbM>Uj5(!N'1=]g?GT9%P6iN(>B2^Z3TgLN%-'WU@n"AV-=DQgI4Yp_Y0EVE,?KAALe9,lG0[0F[:u,FHd8QF4A*.k\uW@=U&[pUI`[1G6A6pa+O)>D-IW=XQ8dl.I<B$,@%t\TLI)*@DmRo+5cP!g`j=nEQPcNp4<d+#8*tGuXLaDC^-TNYX,93%Of:$C4)`pTLdWrrt_bH25gkp>aUL._m&YD:*#-TY;-gQbpQ7"umPJ\\b*)?Z]UX"`h%n$PmG2R-F5ahYWLC^XM_3od?p(\_&a?FEf3c!-Ep[RL06.F7st,]fs`WE=.f8J_;P]WYhJL;FrqB#>D8j[XTSHWcY]*G_jmB3Of@3.#(F)A9AgZ<'_7@YB7u.['/>1?>I]@i:QhSrks:B9X?LK8:5,cbCU+NMDa9_hWb.9"%Z>2.\M&uZj_do$&%-k%pJGO:nAl^R?9;.BY:0ce9L&\[)m(=_bkN8-.eulZ/aZ[mi+q5m54,h<;a>g:V>a_c\@$94;mKdp>,"GYZ3VJQ`m[(?F@/`lc\hrnq&4;S%#nr29jsXB-,J>YMSh=omci77IHXWOQmg*>tSi)j<0)k:J']HUM_#c'ls#:ORiZ;qNG+(c8?;j+*fMUO3WR=HhGcU`<6q!WamVZc=f7g#P[)h\XN:i]TnE>HsA1c<Ws*&QrQabKe@&eO+Nhok$h-:8#[t?bpNSK][hp-Yq+a8&*:7Z`""9A;Z]oXf%!#(P!4VrBaF44(]4tL333qQ=5/.!3+<@XTjfVZ>ZKk;`:%m%JhnJp(@-s+\*se8L^t>/f#J8Ng3^eh\lLG_%iq1So-G+I4W[4'*Fl3Zp9dFi$"r>QW?iCh/ks\PgWC;U=0Eb`pBtDc[Gn(;W\T`QLaE-9G)&:N.NZMU+<nXEIB3R0igl!6K@0$!%QZP!A=.V6(i=!TeNLF&-)]Wj?p96ji"^*.>pRE$:R)3fUuu+U!Zl8ahY\r62ld(r5?+@$fiV#n9TA^j&3jBaq;[-W)`_O\?IT?nd46!d<0LfgOD9ECJs5]%CdoD_;$_0-(V7!(fuR-.f,b=VDEc\?#uBr2nGCV`ksi(9!s^!RU&H>c%.np9Z>2E-EUPic;3^Mh[!t!op3%Uf7ks\Q!QIjLGa@sN(OT7d&3btba?e#?-uR1m@+bGJIIDZ[_97atj3deik'b.;W7t,IIf^%%V\%arE!/N-;*jkj"kBdn4P'nHUeU'4no?J43p"8o%]W[#ED"o!c[IFnW(]ih6"WAUTg0ZOI!jH$_OoTLQk^gcgCF&=cj?XI[pp/gg"qk0&`Nse3I;IP.UO)km5LAZ;Z!EY!0-Len6!X?,)kL.UF+?4Oie)G/cY6^HLX4or(:2~>endstream
+Gb!#]flGh,(4FM1gnu%aN229Zih`8Xfh%]S@RX2SI`Mmb66b,/_#U->FnNf$\KE3!e*\oh;5tV6CbF@bQZLFU!p\bmn@t`Wk^6,Y?Nh-s1ga/-n+:=C9pbGf?X<kXbJ=`#O+Yp,&0:8Q'+XR$R(iQ8<\H./YT"[:@F,G#pZU+f*af84IPc*i\iZ\]3gQ>oD2[PG6I$dM$J2#qLVcT?LU.NCb";GWF?@)5r(Dr')(T-<=gVioNEW(DEg[Y_L&49k6CLYKi8qLOCf6i$J_u91IFk3_;<a7*?N*:_heE'33kDl5(q40>$O9%FHTQ.C^/FboMsSGERDg`rVn.hRWA"qeU*i[Q(/9b5Ue>64gX^4>[email protected]>lZL,!G2:ka?iBrWG%'R010mGKHjShe[IGl&'/[j#MRBnVCA_MiU/<9:d.t0&0Kea^pCDPr:\g4W-_nkHrhPZJ<PEK7DpdqXpL-#IPLK/!(qq%L/8/,jeD?]="bTQH9o>V1<aX-nrj1_Mk]:]jm,u5Rhe1p+Y7mIMHF8b2>Xpa=>fHnJe*Q5+G,2Gg<?dJL>tluL[+=AWRHjG_+m?"^/(+kMBVdC[Jpp"dg[5!,<.6KAVp,Ec@O!**N1csmOiOgW<U0Jb1#0<tb:<Lk-Kq+^K72-@33pCM]2TVr-Hng;d^F'8;RmrJ`lb"JQVn]h@7K!cY^8Uu4Bp"$Hc%!#]aZS9,+7k%@]@VD=[a$i7RS3Q1mUcP3P&9m/QAb*k#XZ;e\*dn=Y+Bpd)*`:"=)=;n7\d80cZRZ@cr8VWlb?oEp;6.mBqftI45=1bPak\Z(noX486;k9",]l@?jl1P*\#Q5XG6C8f6P6I6%T?9K"N^>TDiX0tL0nN@%^l=09qpBdJTN7gC(@^nuCTo/jKsEJ2eNKK"8kr7!qJP)V4b0eE/NeaP*?UYs]TR*ekd1aD=1U0G*bd19aTmECfP163'RbX3H-HAla#Eo13Ii-;ahe+:beg<MRiI:[4uH=+U%Pp"WOCIAQW8*BT)A<%XgcrnlD6Z38Q77\aH;_P4.A`R':(@<mC:)8is*EU?tOI5DJ$fMED!4Se@aJQf3AEVkQKV#To3fuLC<K=^GaJ#"mJ^.39q?)S+0>7\gItH6Z$f(kin\0S<#Q<[XGb?`YS9=.BlSZm)L0NCp^Nc4Ti!dYap/K&3#pH#epMYtn/!r[9;Ak!%;1tma>?Du+.Y2]gI-?9e0#<`)V02oK'cCj?'hK-d74N?4g'&Bs!H-=Tq0,:il(cYHh,<?._YL2Tf_VKncSpDXL>o7CNfCfmnZT0jn1$hfP9M6b,d5<bQptM(0LNBC-tqqZ*Pb!A_Y[)EC:9;=;ebMsN_OW#Dd&GQ,%j0^IElWO7c%Wc1lsU.Ba#;I<^%BB%'s`,*+RPTX`>5VP4J2+CkbH_LUaNP();@A6D]#/LR4i[OSK+Ci]'n3`7#g"Fq,nf?g0>d3l*]jnm83+0O2q==ZD9p"b)4foo(JCd.d5+\7'r@iubDY^&^gAJ:hQ66"ImO^#%<qcS(,)SLM(-o"52=E=>4U&4jr*:5,q,Xb8g;86UiY&L@kh-"mkY_1%9l<[IWIS/?H6W#ipTc\GIga;4TmKS;*4LL24MCklbM>Uj5(!N'1=]g?GT9%P6iN(>B2^Z3TgLN%-'WU@n"AV-=DQgI4Yp_Y0El:<Uj"X!\qnDi*8Ycf^=?oa!HQL\(Y\0gfj!Ph7`j5NDO\FP70QX$,!!K(T'S`k/CB9/-$^cEV$efU/uS9U,h6aUtC8H-6*X*8kLUArefaQ1V,;:n7IXn9Rm>,]Z_XQ5j"8.rVm^V5$o)t6,Sj[6r>O&)Ar!-br["f"5.qGrtg$3TT5^hf7r$B(X.d<TF@\Co:^8>O4,VUKm:mn\9.:<,/VT=C3M@ahR0iM$#Ia;%`b.Mt"dg$_)uni-`j.3R1GB!60Q,)@!b-0`o$0X]'Xrfr/6db/g$+]^,Ti$.BZ(b4@)eS2Z0"]`p_dP4\I^3JO\XXPcPe)q,7'E?<2c,thdnWfq&$a04QepJijV[(]^+A&9,!9TQo1*gJ.%sD/ABY>'HH&>^:6I**6nWr#dj,q[9-ED8"a3Z]0>aM2)NcuGWD(nPgdk'ZYnEZ6%nWNW/)s[CkA?]:kGDhs,<nmg`^;f/Ye^?(R0?<\-$^sb-hVr;$1j?-pGt;_`HHR,Ts68hc\Smp&WH6"#.Ab^]K+%rK-(1Bc\o"BGaOt>qc5Xjg:MM%7f+8dJjLqc\3[9+B`CCjN'1=C0P-%J>UZq[sgjAq\C>qVN<\C9@E@jjf'h#E=(+FMD\tDVZ$;0$AM`joZV5doXT*TQ1;C<*rY't"DPhV'/4")p]#uKS_<Wd&,qA>Bm+i)!%n5E`RY[2Q]mIi^%LcOOLMRaXE<K1K%^Y*$'c?rSnX>fFh_D="k5$rP;T/lsemhGri>.LFmF)_!$=&A,TGU@uaN4"8jf-3p8S<7TbQ=/4R1-sr:.pF]7]nTeI08(L7MOS@Yb"+-.RKQ=:*W?bLGS$KtL]j(;YnF-pG:n1N7^GlMkhH0$@H)]E[&(<7pY\)5S:2E.q%2tF=0thIN>P#F<?ef5Sq"iiGe@m=n$b"+XO@"F9SnX;@m,N"2:ff6r+oBqNt_@qjIE2o;(iKL2\c^;WlZtD8@?92_+FE#p*("+."jS'$XMKOBOn-NCQAYf2P4dZKW"N)q!I;1E22.(JV+Nd;#c[l#("!-feo:R\G(@"W6'5ng-;$GH^bT$PF24%!95D64Ni$8lTM:"#Tko\jIg(0'K>,G0^Sf6[eE#6i8qqunS!Aoo=eX.PjKju561u#6\t293!,fRW1a!pK'HDGT'HM_dO+TUH+B;USatH@#K87=32LH!ANprE<2LtE+>Y.;:`k`a5!J7MiQl@abR0KmD23LUkQFk`gT_+DD!sdO#k<MCS3;a8PqS,q[[ToRW.!0<JAugCGc]m0&]X>(;%mWU87`"4Q@F*?^%`Zj-qWn~>endstream
endobj
endobj
xref
xref
0 12
0 12
@@ -87,7 +87,7 @@ xref
@@ -87,7 +87,7 @@ xref
trailer
trailer
<<
<<
/ID
/ID
-[<3cb1e6404bca482100e41159cff17f6c><3cb1e6404bca482100e41159cff17f6c>]
+[<858ee61722a5f5c2c69b17e80403fd90><858ee61722a5f5c2c69b17e80403fd90>]
% ReportLab generated PDF document -- digest (opensource)
% ReportLab generated PDF document -- digest (opensource)
/Info 8 0 R
/Info 8 0 R
new file mode 100644
new file mode 100644
@@ -0,0 +1,99 @@
@@ -0,0 +1,99 @@
+%PDF-1.4
+%���� ReportLab Generated PDF document (opensource)
+1 0 obj
+<<
+/F1 2 0 R /F2 3 0 R /F3 4 0 R
+>>
+endobj
+2 0 obj
+<<
+/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
+>>
+endobj
+3 0 obj
+<<
+/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
+>>
+endobj
+4 0 obj
+<<
+/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
+>>
+endobj
+5 0 obj
+<<
+/Contents 10 0 R /MediaBox [ 0 0 612 792 ] /Parent 9 0 R /Resources <<
+/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
+>> /Rotate 0 /Trans <<
+
+>>
+ /Type /Page
+>>
+endobj
+6 0 obj
+<<
+/Contents 11 0 R /MediaBox [ 0 0 612 792 ] /Parent 9 0 R /Resources <<
+/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
+>> /Rotate 0 /Trans <<
+
+>>
+ /Type /Page
+>>
+endobj
+7 0 obj
+<<
+/PageMode /UseNone /Pages 9 0 R /Type /Catalog
+>>
+endobj
+8 0 obj
+<<
+/Author (Alex Frison) /CreationDate (D:20261021063644-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261021063644-07'00') /Producer (ReportLab PDF Library - \(opensource\))
+ /Subject (IT and Technical Support: Linux and servers, hardware troubleshooting, customer support and escalations) /Title (Alex Frison - Resume) /Trapped /False
+>>
+endobj
+9 0 obj
+<<
+/Count 2 /Kids [ 5 0 R 6 0 R ] /Type /Pages
+>>
+endobj
+10 0 obj
+<<
+/Filter [ /ASCII85Decode /FlateDecode ] /Length 3089
+>>
+stream
+Gb!#]?$?rB&q/qEeD#WsgXtns3;^!.#tnk:"P^`Y3I-bAZ7XM\lB0S9bi-V2/ZIRO\&jdh#N.S=5GV1Wh-7?</Sa9`bC@M\dH_+PHIJTh7$BX=3fOVDhU1lS`Et9%jceLA0(u&@@imA^HDB02T0D*1BbiA+=K>W?9p69,8(?8U[A+M@]JiOF'tl?ElKffk^=)'a]\>sS]sV?gd'%VOb+3GK]Y,Q6h[c3bB[@#4MScMC;;&hNX@?QOFM9]"_RW't0N+FUV!khCM7WK5me,o5_4Zi^!g`hD5N^hV7ODDdou7.dHm*,PCW&8^V6X0MPJ1t\[^T(N=\Se=&%TnJX/Rq!a2r\aVu"!TDfdLR2u,N?di\O!66_oH7)mDD`?M!qOltH."+$lNil^/cdTjYj)D-Z2Xl'n67MjNAg83@1SuY^n=Jo)3aG/=B^$:^-@U\5[c?:51dsgT0U3fYdp*Uf/3^"LDrjusd(,@'"fV91!rkVsY^X9f'AH(PP3WmZp-G_q@/^s)Mg$i<lRu<i!MZI+1DrEc^f,;+K-juG7,8+Q.WJk_jCBLibBJ"qCA;VqU*D4qZ<uE0^EfmJRZ^V4Ai(Gi/:CEH_#iUV.a25r8XBM7oNTa:UlrqJ`)J';AKnMEHdUP&\W^Or[`Y3_3&Ou[%c&3iuQ#e1O1I^X;j@cKg`[:bgORVm:)GN#TkYs$MUe8-AS=i8SgRt78@4K"M`K?`9f4%B#=Su'#V\X).b_QX:pscjGeS&REV5tK$$3sgMF(&A,H7%f1G1L"R;\JQ2V+drWSYjg!nNTV7;fm'TE>XWHR7PnJrWrbc3NWrf)Xl\(f'6@G/PCf:JeFY]j0^`gZSYRGYn<![9;D81?^-ZQ%i2*7DD,A^9EJ[K,-kUNLgrQBNUiEq::3#^e'M8DK#eq&_QF&9fYj=$P<"WiYFUeaf7;s=*>^/BdqT1n:9'lJlKS@f`bOXS)Wfnl*.)/FWR_m%RLQF)MJYJ9WG^n@`S_=Bs6&J!]CCmjgOj,>EN=dh^W]FGlnU9D$D;3HM:m[1#KsSMGag>sr->GRIr@u^5&prBe`_;d!YBn`mP(f[Q[13EEb0DDKPV=A._(t@q_Y6W:PmebOSWfQ!IrnZSc'VbqL!/^Y%2;9ECi_`J\/?#3o>:?`!GGs$Ij4tZ6Us5lrOEF*9m5bRMm1n\sU_UcYG7b-PS08'NHbIhg6s7MeOBL8U;Cc_%D0UgGD#SZ+Mo7<4ubEMV<6aXl%eWl!5>%7%_/ZNeo>AS=JHr]=5ZiA`9W"1Er,m^M0rT0M@r)iU2@:N)+MMbJ2-_R%<?#,*PftpGdQ%_;[NZ=f4'#<qO="jdbRd$O`m@n)\oAHF@ui:@:?bV$O9@jN0HLiBK1<Isb=mZaXR,O%=>+:,Ol]Rb?Csrs2AGHkjg-n"&^.1fZ/U/==UfdHB-!*N+rZcO#IbWUiO7F4Nrr)<h-:/o(IhR4T%@q5#>E&W.7@>/X)&5R7[YP+GU-bK#'k1o.#Aq/9hpIT8CC>LcSrJSP<&g>RTp[.1rXZi9KeB?-d2j7e>L]/FO*4R]cRUJ9nM\E3T,b6O%*;SD`i_:!DfH0UbG80V$rH\XXl&3@h#:U!3Ni5\XEmPgclhfoO]W&>*t8ZgF:O_!j0An5!p-u)47:4k?1r`Z.C^l%>.AOV8m>D^s4F5'2JQI4ltIc'UJNIK*pXd^7=)XYu!Tk?FgAM#[!^/l[TQ01HWd1HL`JdaS3fW;"+>apsgk/A!_*,K+M;],rr60Z:kE6F'*AdQ`T/P*lXHaZ1InS2RY>;>3s5N"t;5_(3hic6%e+OITq_o$e7*8#O>Bdi>B:gj`ZKkVr1)?q]JX67>ZBXU%DF"rQ@h9&&7Mj^_d:17ih\XQh8KE:e\<,r.8MMs!#Y*28WHsrX&\fm!JkWoS;Ks_X,:)X@0`#=eX,jQo`c*0CAecC^M]U;p-q`4ZZXKg/lV`$9C+S4,LQ_grZc`KRPD]d-QjJm:PN!@J5H3L_qA9%;66I`SL5i-X5NZB[]jDDHJ`OSdfGYXI%SEOY@IG:9++O'NQ[tAGu8#TeF:GIJA`sNV;7]^Zuip_I("0BF_VP8ul/mgupNQnmpfNKlHjm5\:e[gbJU%Ff-&(pJE**k^p>9iKQMKmmQL5d;*(FVQN8-'-#gd]a^=N\3_J6.dL.[8L,FA!\\"-o$F@6X#Htus5of\WP7A-;ZkrcpI-2,XNo(3b&-4.GX+V`i7:c;f<-u.F3oE[/P'lg=O4jBK-oRee2,M5>'XMmP2I+_V_V'?\ocJVKN-XsDUUK5h?b1+"%Z:qIui%S7;-tb,j4*Y$QK@#"akmXF]QEdkGESAV&Lga\R\crF=?^Jn"Q4P6KA15qZ[8KHB<$Ye#&)%!\EJlD:cZU/0U3tK`!c%M&+<cGS<g$@[X<CLp@`:]PP"jZ>26(TW]P5'>T_MnZPC(biqH4&*,@^AJf[Acq$fZ==tcO]m1\^Q\&ng/AQNGL.faV2[:Qbs4Cpq^k/poIP3E@;tOjejh]C9N`UKS$#@Q(SbdG@\3dZZ.Z\4DCObjGmeoG?52ok'@<tiC"-m%Md+MhrJ%oa_J!Q;EL<#F['A&#PSr13&4r))dnd^;`eV*AqkTKA!IgbJCDfcq?+')O4^#KWQep6VK.%`@EUSEsLff>;5!C7E&NF2_e>^ll+-[3g/Btn!o*mGDQ,(M&!<S8FX;_Mt&BiO1ukcW+fJ`L6kcZBAtKn-IML#p@l9EjXcro#=mf-Q:l+CjJ>"f_YtO.?Rph:Gd.,+]mC_@s#k4_gim?.^+&R0?C:]M&09YD*t5jc8#P7l;U"rRfBkM(P`.#b:ABP!nAs]t2^I\P[B*&`fmVZd#04XFrL&m<!.0s-V4tM">&4#btG78[LC/#$W77C:@ib5D]8Mo@^?T9oTf`AFPj;.3AA'`]eIZj2U`'^FnkG2j_n+4XZm!:s=g&Rebub>82fl#7r<I-t:KX13VQl"<'F^i&10aPUV(X`f/ctQ<XY(F)BdZ*!>!BGNkI^I:Li8`2+inI^S[lD\[$;HlXm&_Du>9425P"^Kp=F[t1L5$F`A&F!o-AL<Uj3pm2!E^ol`=5gtYk!ICk4q>~>endstream
+endobj
+11 0 obj
+<<
+/Filter [ /ASCII85Decode /FlateDecode ] /Length 2105
+>>
+stream
+Gb!#\gN(b6&:Ml+o[@]$f/Aq5M;cQ%!C@A(Z!"*8p15G-X]^]s`69XL'Hb)f8%'$Qi(u[=P#O!u17-If:S1ZS?2Q&>Ip*CHT$`0<#pha'bpaNP"uLZm-aUL1HcbEqi<UeAqV=mW&)_i542T;EIK&IHHPe;J`p\$[Bi\X6*f]t,_tQ2:*e&Uu^%+IV5\IriKlls;VF!pO(UA/29+s]i=0b8E9;asU6Z86R\F0BifmbjUP?7Mt^Rq\b3Q)@!.XRZ8IJ3Ku.S'.]q_X=K9K$\QFaIC$QLNT-p8k%Z@#N9hTmN"44%VRnnEnd,nBbMIOS8]K%M%K7R-JQS-_F-jmdrG7a1:msKNZHV6t+>l67s\hnkQ%?GE:RF<(]*YY)pr<.(+fi!qc/SjSMp!9]7E_)KM?n=4n]YM:>\sWQ_K,Gc9-/psp4SI?CB4PBA?]F#M)r-j3A6N987gP@DVY%R<NXR!T2(\[)H_Pqb53i:/%".+4UdV]\DoG"r.Ro8oInJU$U![E'e2>LQ>tcs0f@lA$3k7`d<Kf!M`#l#U(cKGGAp0P!gj3*P$^#$I?E7*N5bHs6AGBYff->pp5<pjZ(:SlO&9`Kefn[PAnn"GfLCK5,15,Y4<Vb(OOuRCfuAOUVEXqY"/S+37*WR<(4.7A*mj.\$m*Z-PiFc,o@1:2-Ib%GH8j9IP8k23Y$1GCd,FYU/^5:rt*;@0UTH8B:9nC4r.=Vjhg'Z9jrp]il<?]dSS;\*1ehUE'_97ZEB,\3MB.VY9h`^th3EE1G_@P^Zjd*=unXf0Q7),qZ>LJmD-I92<];\(g1k]75qI^M"bt$!t.maQ>j/F!3\Z>RT_J9Cips.6lCX9_eP6$CB:2dOI]Rb,f#S.9nQ_lf@unGA&5P2/aY,:T[o98QZ?i`ahD7+o]BgTE;.P%Q4ee#PU0nVc(jqQQ+j=:fuIOYcK0iiecN]/\o.<0EqR=!kpib2L;c=FbmOmmUU_)pt/?8Z<h5k9j40!\`BKob,ps=kXZ@806DR\O#f6d]U)k;Bc@agN]S]SE,]$pC3"Zjco6"C>>UptLE/@CaYYp`gS<ZiETNm?W4HnE(0[dE<aPlYO7`(gAlGYC`3F\@eiQpd5Q$rFhl@2a1>FU"d_G7m+%m"4ILY6$m&jp.`U65#o`8GNduKb?C5jgpL,GB&W.6#,lXX"QMWCnbR^+bESLVMjS$A(8p4e2ogidXPE&bSH8UMTR$tpDOPW>NQ$<WRiFb)<khdd\6$0DlbW8Ws`M)At#"WfD-U="N">2enhi0[X,q9d?`&<X\(QoX2NTlr#9G;$nPD';ZcI#NLICT0@?EH3lt3MuD+=3kkCh)4r"0``M&iSnoBW;eKAm[8<@6%e\E0Qp%:o[[II^IGnTO7"p%[79h5C"/pigm,R(7/P&ERT:of$@@CRq[[D@hU>.4n1J>h`GJ=Lo*h*I3&2ihMjg(@KcXaLXU3H#b-PQr7p2_<UXt]#O3*TlL'YV!>o-Wm9CU+U_4_u(;%KJ=3-g(4dA"fV0B!`9,3/hor15,3CiV8JIf1i0T4mk!%[`qD0"InO*/do>;c/:l7?HFCeU/AJ)9D#b5-VH2aRIB(o.^"]mO@G<bTkW"DD9,UmEEBKnSFQB@^3P7lQ8#Zq>Gs=j_!Oe'gP*@;Hp*=7[a0U./f3K[[l*:#n#5@qYj#g_$'qR$oup/G&8QE)&Nfb`QYI=Xtk-4dXZXRauV7\gcjjqb4\FFbX?^<db6"LG[+V.V*o[p_aZGi>_mK],o$Jfae<Ko1>ShZ;Be3U4KaMK';l-CG-SZr2Yo:L21A0McHZ_#&t>[Qd$dd(DbZ2b6)'"Sg6XQ^^KgSb!!2q*-/b*kC<JLSN[cEdK$P1M,8,T8=^YKE9.5YpA@DL-[=*6rU38g_k^EKSZ`dB(PN+SlPM,3$QTO?rO.03Z5>[(18b0P8D8NIk)T+8](kt=^8r_)(SU=2Y<-1Vj1p'2b-QR:o2!+GeLZftCKMF%'iTS32i#U0D*B>"lH[>!WfY/r*U@Z@S%`O/H#ad6kh*@;PdM]2J^%VD6+gI8efA98j\J%.dppS>eedWR^ULf?7iAjr[,L2t78q84d0LbqtiUak6hZ#r2W"/~>endstream
+endobj
+xref
+0 12
+0000000000 65535 f
+0000000061 00000 n
+0000000112 00000 n
+0000000219 00000 n
+0000000331 00000 n
+0000000446 00000 n
+0000000640 00000 n
+0000000834 00000 n
+0000000902 00000 n
+0000001275 00000 n
+0000001340 00000 n
+0000004521 00000 n
+trailer
+<<
+/ID
+[<a8f118002d6fd8b1daaf8b897f8fb0a6><a8f118002d6fd8b1daaf8b897f8fb0a6>]
+% ReportLab generated PDF document -- digest (opensource)
+
+/Info 8 0 R
+/Root 7 0 R
+/Size 12
+>>
+startxref
+6718
+%%EOF
new file mode 100644
new file mode 100644
Binary files /dev/null and b/public/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf differ
Binary files /dev/null and b/public/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_Oct_2026_68186113.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_Oct_2026_68186113.pdf and /dev/null differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_amber_38b37ff8.pdf differ
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_amber_38b37ff8.pdf differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_Themed_amber_68186113.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_Themed_amber_68186113.pdf and /dev/null differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_cyan_38b37ff8.pdf differ
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_cyan_38b37ff8.pdf differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_Themed_cyan_68186113.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_Themed_cyan_68186113.pdf and /dev/null differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_green_38b37ff8.pdf differ
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_green_38b37ff8.pdf differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_Themed_green_68186113.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_Themed_green_68186113.pdf and /dev/null differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_indigo_38b37ff8.pdf differ
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_indigo_38b37ff8.pdf differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_Themed_indigo_68186113.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_Themed_indigo_68186113.pdf and /dev/null differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_lime_38b37ff8.pdf differ
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_lime_38b37ff8.pdf differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_Themed_lime_68186113.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_Themed_lime_68186113.pdf and /dev/null differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_orange_38b37ff8.pdf differ
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_orange_38b37ff8.pdf differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_Themed_orange_68186113.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_Themed_orange_68186113.pdf and /dev/null differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_pink_38b37ff8.pdf differ
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_pink_38b37ff8.pdf differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_Themed_pink_68186113.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_Themed_pink_68186113.pdf and /dev/null differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_red_38b37ff8.pdf differ
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_red_38b37ff8.pdf differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_Themed_red_68186113.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_Themed_red_68186113.pdf and /dev/null differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_teal_38b37ff8.pdf differ
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_teal_38b37ff8.pdf differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_Themed_teal_68186113.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_Themed_teal_68186113.pdf and /dev/null differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_violet_38b37ff8.pdf differ
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_violet_38b37ff8.pdf differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_Themed_violet_68186113.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_Themed_violet_68186113.pdf and /dev/null differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_yellow_38b37ff8.pdf differ
Binary files /dev/null and b/public/Alex_Frison_Resume_Themed_yellow_38b37ff8.pdf differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_Themed_yellow_68186113.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_Themed_yellow_68186113.pdf and /dev/null differ
@@ -1,14 +1,15 @@
@@ -1,14 +1,15 @@
#!/usr/bin/env python3
#!/usr/bin/env python3
"""Build plain, one-column résumés for applicant tracking systems, from the same content as the site and the designed
"""Build plain, one-column résumés for applicant tracking systems, from the same content as the site and the designed
-PDF (build_resume.py), in two versions aimed at different roles:
+PDF (build_resume.py), in versions aimed at different roles:
public/Alex_Frison_Resume_Engineering.pdf software engineering (full-stack, payments, identity, streaming)
public/Alex_Frison_Resume_Engineering.pdf software engineering (full-stack, payments, identity, streaming)
public/Alex_Frison_Resume_Data_Analyst.pdf data / BI / operations analyst
public/Alex_Frison_Resume_Data_Analyst.pdf data / BI / operations analyst
+ public/Alex_Frison_Resume_IT_Support.pdf IT, help desk and technical support (near-home jobs)
The designed PDF is multi-column with icons; a tracking system reads its text layer column by column and scrambles it.
The designed PDF is multi-column with icons; a tracking system reads its text layer column by column and scrambles it.
These have one column, real text in reading order, standard fonts and plain section names, so a parser gets every
These have one column, real text in reading order, standard fonts and plain section names, so a parser gets every
line right. Facts only come from src/data/profile.json and src/content/; what differs per version is the headline, the
line right. Facts only come from src/data/profile.json and src/content/; what differs per version is the headline, the
-summary, which skills lead and how many bullets each role keeps.
+summary, which skills lead and which bullets each role keeps (a count, or the bullets' indexes).
Run from the repo root: python3 resume/build_ats_resume.py
Run from the repo root: python3 resume/build_ats_resume.py
"""
"""
@@ -79,6 +80,32 @@ VERSIONS = {
@@ -79,6 +80,32 @@ VERSIONS = {
"founder": 2, "earlier": 1},
"founder": 2, "earlier": 1},
"projects": ["irdr-autonomous-bot", "irdr-quality-recap", "amazon-userscripts"],
"projects": ["irdr-autonomous-bot", "irdr-quality-recap", "amazon-userscripts"],
},
},
+ "support": {
+ "file": "Alex_Frison_Resume_IT_Support.pdf",
+ "headline": "IT and Technical Support: Linux and servers, hardware troubleshooting, customer support and escalations",
+ "summary": (
+ "Hands-on technical generalist in Arlington, WA. Ten years running my own company's servers: self-built "
+ "and colocated 1U and 2U hardware, Linux, nginx and DDoS resilience, and later a full infrastructure "
+ "migration with zero downtime and zero data loss. At Amazon Robotics I investigated recurring pod, drive-unit "
+ "and workstation defects and escalated them to RME; as an ICQA data analyst I built tools other associates "
+ "used every day. Three years at Verizon Wireless on phones, plans and accessories, promoted to Assistant "
+ "Manager running inventory and escalations. I document what I fix: incident write-ups, runbooks and "
+ "support docs."),
+ "skills": [
+ ("Support and troubleshooting", ["hardware and electro-mechanical troubleshooting", "root-cause analysis",
+ "incident documentation", "escalations", "support docs and runbooks"]),
+ ("Systems", ["Linux", "server hardware (self-built and colocated 1U / 2U)", "nginx", "systemd", "Docker",
+ "Cloudflare", "cloud storage (Backblaze B2 / R2 / S3 APIs)", "zero-downtime migrations"]),
+ ("Network and web", ["HAR network-trace debugging (HARTools)", "DDoS resilience", "WebSockets", "TURN / coturn"]),
+ ("Scripting and automation", ["Python", "JavaScript / TypeScript", "SQL", "PHP", "TamperMonkey userscripts",
+ "Excel / Power Query / VBA"]),
+ ("People", ["customer retention and consultative sales", "coaching a team of 4 to 8", "opens, closes and inventory",
+ "technical writing"]),
+ ],
+ "bullets": {"PowerChat": 1, "OpenVibe": [0, 2], "Amazon/analytics": [0, 2], "Amazon/operations": 2,
+ "Verizon Wireless": 3, "founder": 2},
+ "projects": ["amazon-userscripts", "hartools", "openvibe-cutover", "mp-select-mini"],
+ },
}
}
@@ -105,12 +132,14 @@ def plain(s: str) -> str:
@@ -105,12 +132,14 @@ def plain(s: str) -> str:
.replace("‘", "'").replace("’", "'").replace("“", '"').replace("”", '"'))
.replace("‘", "'").replace("’", "'").replace("“", '"').replace("”", '"'))
-def bullets_for(role: dict, counts: dict) -> int:
+def bullets_for(role: dict, counts: dict) -> list[str]:
key = role["company"] if role["company"] in counts else (
key = role["company"] if role["company"] in counts else (
f"Amazon/{role.get('kind')}" if role["company"] == "Amazon" else role.get("kind", ""))
f"Amazon/{role.get('kind')}" if role["company"] == "Amazon" else role.get("kind", ""))
if role["company"].startswith("OpenVibe"):
if role["company"].startswith("OpenVibe"):
key = "OpenVibe"
key = "OpenVibe"
- return counts.get(key, counts.get(role.get("kind", ""), 2))
+ pick = counts.get(key, counts.get(role.get("kind", ""), 2))
+ bullets = role.get("bullets", [])
+ return [bullets[i] for i in pick if i < len(bullets)] if isinstance(pick, list) else bullets[:pick]
def build(version: str, data: dict) -> Path:
def build(version: str, data: dict) -> Path:
@@ -143,11 +172,11 @@ def build(version: str, data: dict) -> Path:
@@ -143,11 +172,11 @@ def build(version: str, data: dict) -> Path:
story.append(Paragraph(f"<b>{plain(group)}:</b> {plain(', '.join(items))}", st["body"]))
story.append(Paragraph(f"<b>{plain(group)}:</b> {plain(', '.join(items))}", st["body"]))
story += [Paragraph("EXPERIENCE", st["h"]), rule()]
story += [Paragraph("EXPERIENCE", st["h"]), rule()]
for r in data["roles"]:
for r in data["roles"]:
- n = bullets_for(r, v["bullets"])
+ chosen = bullets_for(r, v["bullets"])
head = Paragraph(plain(f"{r['role']}, {r['company']}"), st["role"])
head = Paragraph(plain(f"{r['role']}, {r['company']}"), st["role"])
meta = Paragraph(plain(" | ".join(x for x in [r.get("location", ""), f"{r['start']} to {r['end']}"] if x)), st["meta"])
meta = Paragraph(plain(" | ".join(x for x in [r.get("location", ""), f"{r['start']} to {r['end']}"] if x)), st["meta"])
- items = [Paragraph(plain(b), st["bullet"], bulletText="•") for b in r.get("bullets", [])[:n]]
- if r.get("stack") and version == "engineering" and n >= 2:
+ items = [Paragraph(plain(b), st["bullet"], bulletText="•") for b in chosen]
+ if r.get("stack") and version == "engineering" and len(chosen) >= 2:
items.append(Paragraph(plain("Stack: " + ", ".join(r["stack"])), st["meta"]))
items.append(Paragraph(plain("Stack: " + ", ".join(r["stack"])), st["meta"]))
story.append(KeepTogether([head, meta, *items[:1]]))
story.append(KeepTogether([head, meta, *items[:1]]))
story += items[1:]
story += items[1:]
@@ -1280,8 +1280,9 @@ def main() -> None:
@@ -1280,8 +1280,9 @@ def main() -> None:
json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8")
json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8")
# the one-column versions for tracking systems (build_ats_resume.py) live next to these: never swept
# the one-column versions for tracking systems (build_ats_resume.py) live next to these: never swept
+ from build_ats_resume import VERSIONS as ATS_VERSIONS
keep |= {p for p in (ROOT / "public").glob("Alex_Frison_Resume_*.pdf")
keep |= {p for p in (ROOT / "public").glob("Alex_Frison_Resume_*.pdf")
- if p.name in ("Alex_Frison_Resume_Engineering.pdf", "Alex_Frison_Resume_Data_Analyst.pdf")}
+ if p.name in {v["file"] for v in ATS_VERSIONS.values()}}
clean_old_files("Alex_Frison_Resume_*.pdf", keep)
clean_old_files("Alex_Frison_Resume_*.pdf", keep)
print(f"\ncontent hash: {h} (filenames only change when the résumé content does)")
print(f"\ncontent hash: {h} (filenames only change when the résumé content does)")
print(f"text font: {FONT['label']}; icons: Font Awesome 4")
print(f"text font: {FONT['label']}; icons: Font Awesome 4")
@@ -1,6 +1,11 @@
@@ -1,6 +1,11 @@
---
---
import profile from "../data/profile.json";
import profile from "../data/profile.json";
-const sites = profile.network;
+// The "sites" tile stands for everything past the six core services (profile.networkLive); its
+// text carries {more} and {total} so the counts come from that list and never drift.
+const more = profile.networkLive.length;
+const total = profile.network.filter((s) => s.key !== "sites").length + more;
+const fill = (t: string) => t.replace("{more}", String(more)).replace("{total}", String(total));
+const sites = profile.network.map((s) => ({ ...s, name: fill(s.name), blurb: fill(s.blurb), stat: fill(s.stat) }));
const big = sites[0];
const big = sites[0];
const rest = sites.slice(1);
const rest = sites.slice(1);
const icons: Record<string, string> = {
const icons: Record<string, string> = {
@@ -17,7 +22,7 @@ const nodes = [
@@ -17,7 +22,7 @@ const nodes = [
{ key: "tools", label: "Tools", x: 400, y: 50 },
{ key: "tools", label: "Tools", x: 400, y: 50 },
{ key: "games", label: "Games", x: 400, y: 372 },
{ key: "games", label: "Games", x: 400, y: 372 },
{ key: "community", label: "Community", x: 670, y: 90 },
{ key: "community", label: "Community", x: 670, y: 90 },
- { key: "sites", label: "13 Sites", x: 670, y: 330 },
+ { key: "sites", label: `${more} more`, x: 670, y: 330 },
];
];
const edges = [
const edges = [
["live", "media"],
["live", "media"],
@@ -61,7 +66,7 @@ const edges = [
@@ -61,7 +66,7 @@ const edges = [
<div class="arch card" data-reveal>
<div class="arch card" data-reveal>
<div class="arch__copy">
<div class="arch__copy">
<div class="eyebrow"><i class="fa-solid fa-diagram-project" aria-hidden="true"></i> How it fits together</div>
<div class="eyebrow"><i class="fa-solid fa-diagram-project" aria-hidden="true"></i> How it fits together</div>
- <h3>One login, one wallet, seven products that feel like one.</h3>
+ <h3>One login, one wallet, {total} sites that feel like one.</h3>
<details class="arch__more" open>
<details class="arch__more" open>
<summary><span class="arch__more-closed">Read more</span><span class="arch__more-open">Show less</span></summary>
<summary><span class="arch__more-closed">Read more</span><span class="arch__more-open">Show less</span></summary>
<p>Sign in once and every OpenVibe site knows who you are, what you own and what you look like. The identity service in the middle handles accounts, security and the shared virtual currency; every other product plugs into it through clear, documented APIs. That's the same playbook I bring to any company: secure single sign-on, payments and APIs designed so new products can be added without breaking the old ones.</p>
<p>Sign in once and every OpenVibe site knows who you are, what you own and what you look like. The identity service in the middle handles accounts, security and the shared virtual currency; every other product plugs into it through clear, documented APIs. That's the same playbook I bring to any company: secure single sign-on, payments and APIs designed so new products can be added without breaking the old ones.</p>
@@ -73,7 +78,7 @@ const edges = [
@@ -73,7 +78,7 @@ const edges = [
<li><i class="fa-solid fa-shield-halved"></i> Every migration is rehearsed as a dry run before it touches real users</li>
<li><i class="fa-solid fa-shield-halved"></i> Every migration is rehearsed as a dry run before it touches real users</li>
</ul>
</ul>
</div>
</div>
- <svg class="arch__svg" viewBox="0 0 800 420" role="img" aria-label="Diagram: OpenVibe.Network at the center connected to Live, Media, Tools, Games, Community and 13 static sites; Live also talks to Media.">
+ <svg class="arch__svg" viewBox="0 0 800 420" role="img" aria-label={`Diagram: OpenVibe.Network at the center connected to Live, Media, Tools, Games, Community and ${more} more sites; Live also talks to Media.`}>
<defs>
<defs>
<linearGradient id="agrad" x1="0" y1="0" x2="1" y2="0"><stop offset="0" stop-color="var(--accent)"/><stop offset="1" stop-color="var(--accent-2)"/></linearGradient>
<linearGradient id="agrad" x1="0" y1="0" x2="1" y2="0"><stop offset="0" stop-color="var(--accent)"/><stop offset="1" stop-color="var(--accent-2)"/></linearGradient>
<filter id="aglow" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="6" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<filter id="aglow" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="6" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
@@ -23,11 +23,19 @@ const netIcon: Record<string, string> = {
@@ -23,11 +23,19 @@ const netIcon: Record<string, string> = {
games: "fa-solid fa-gamepad", community: "fa-solid fa-comments", sites: "fa-solid fa-globe",
games: "fa-solid fa-gamepad", community: "fa-solid fa-comments", sites: "fa-solid fa-globe",
};
};
+// Everything past the six core services: live sites, shown as chips under the bento.
+const liveMore = profile.networkLive;
+const liveTotal = profile.network.filter((s) => s.key !== "sites").length + liveMore.length;
+const liveGroups = [
+ { label: "Also live", icon: "fa-solid fa-circle-check", items: liveMore.filter((n) => n.group === "people") },
+ { label: "For builders", icon: "fa-solid fa-code", items: liveMore.filter((n) => n.group === "builders") },
+];
+
const groups = [
const groups = [
{
{
key: "openvibe", tone: "a", anchor: "network", icon: "fa-solid fa-diagram-project",
key: "openvibe", tone: "a", anchor: "network", icon: "fa-solid fa-diagram-project",
- title: "The OpenVibe network", blurb: "Seven live services behind one sign-in, all built and run by me since 2026.",
- count: profile.network.length, allHref: "/portfolio/?cat=openvibe,infra", allLabel: "All OpenVibe case notes",
+ title: "The OpenVibe network", blurb: `${liveTotal} live sites behind one sign-in, all built and run by me since 2026.`,
+ count: liveTotal, allHref: "/portfolio/?cat=openvibe,infra", allLabel: "All OpenVibe case notes",
items: [] as { href: string; external: boolean; image?: string | null; icon: string; title: string; sub: string; text: string; meta: string; soon?: boolean }[],
items: [] as { href: string; external: boolean; image?: string | null; icon: string; title: string; sub: string; text: string; meta: string; soon?: boolean }[],
},
},
{
{
@@ -73,16 +81,16 @@ const groups = [
@@ -73,16 +81,16 @@ const groups = [
<a class="wk-group__all" href={g.allHref}><i class="fa-solid fa-grip" aria-hidden="true"></i> <span>{g.allLabel}</span> <i class="fa-solid fa-arrow-right wk-group__arrow" aria-hidden="true"></i></a>
<a class="wk-group__all" href={g.allHref}><i class="fa-solid fa-grip" aria-hidden="true"></i> <span>{g.allLabel}</span> <i class="fa-solid fa-arrow-right wk-group__arrow" aria-hidden="true"></i></a>
</header>
</header>
{g.key === "openvibe" && <OpenVibeBento />}
{g.key === "openvibe" && <OpenVibeBento />}
- {g.key === "openvibe" && (
- <div class="wk-planned" aria-label="Planned OpenVibe sites">
- <span class="wk-planned__label"><i class="fa-solid fa-clock" aria-hidden="true"></i> Coming soon</span>
+ {g.key === "openvibe" && liveGroups.map((lg) => (
+ <div class="wk-planned" aria-label={`${lg.label}: more OpenVibe sites`}>
+ <span class="wk-planned__label"><i class={lg.icon} aria-hidden="true"></i> {lg.label}</span>
<ul class="wk-planned__list">
<ul class="wk-planned__list">
- {profile.networkPlanned.map((n) => (
- <li><a href={n.url} target="_blank" rel="noopener" title={`${n.name}: ${n.role} (placeholder page)`}>{n.name.replace("OpenVibe.", ".")}<small>{n.role}</small></a></li>
+ {lg.items.map((n) => (
+ <li><a href={n.url} target="_blank" rel="noopener" title={`${n.name}: ${n.role}`}>{n.name.replace("OpenVibe.", ".")}<small>{n.role}</small></a></li>
))}
))}
</ul>
</ul>
</div>
</div>
- )}
+ ))}
<ul class="wk-grid" data-stagger hidden={g.items.length === 0} style={`--cols:${Math.min(Math.max(g.items.length, 1), 4)};--cols-t:${Math.min(Math.max(g.items.length, 1), 2)}`}>
<ul class="wk-grid" data-stagger hidden={g.items.length === 0} style={`--cols:${Math.min(Math.max(g.items.length, 1), 4)};--cols-t:${Math.min(Math.max(g.items.length, 1), 2)}`}>
{g.items.map((it) => (
{g.items.map((it) => (
<li class={`wk-card ${it.image ? "wk-card--img" : ""} ${it.soon ? "wk-card--soon" : ""}`}>
<li class={`wk-card ${it.image ? "wk-card--img" : ""} ${it.soon ? "wk-card--soon" : ""}`}>
@@ -167,8 +175,9 @@ const groups = [
@@ -167,8 +175,9 @@ const groups = [
.mono { font-family: var(--font-mono); }
.mono { font-family: var(--font-mono); }
.wk-card__soon { margin-left: auto; display: inline-flex; align-items: center; gap: 5px; font-size: .6rem; letter-spacing: .08em; text-transform: uppercase; padding: 2px 8px; border-radius: 999px; color: var(--accent-3); background: rgba(var(--accent-3-rgb), .12); border: 1px solid rgba(var(--accent-3-rgb), .35); white-space: nowrap; }
.wk-card__soon { margin-left: auto; display: inline-flex; align-items: center; gap: 5px; font-size: .6rem; letter-spacing: .08em; text-transform: uppercase; padding: 2px 8px; border-radius: 999px; color: var(--accent-3); background: rgba(var(--accent-3-rgb), .12); border: 1px solid rgba(var(--accent-3-rgb), .35); white-space: nowrap; }
.wk-card--soon .wk-card__link { border-style: dashed; }
.wk-card--soon .wk-card__link { border-style: dashed; }
- /* Planned domains: every one already has a truthful "coming soon" page, so the chips link there. */
- .wk-planned { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-top: 14px; padding: 12px 14px; border-radius: 14px; border: 1px dashed var(--border-strong); background: var(--surface); }
+ /* The rest of the live network, as chips that open each site (2026-10: every one of them serves its product). */
+ .wk-planned { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-top: 14px; padding: 12px 14px; border-radius: 14px; border: 1px solid var(--border); background: var(--surface); }
+ .wk-planned + .wk-planned { margin-top: 8px; }
.wk-planned__label { display: inline-flex; align-items: center; gap: 7px; font: 600 .68rem var(--font-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--accent-3); }
.wk-planned__label { display: inline-flex; align-items: center; gap: 7px; font: 600 .68rem var(--font-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--accent-3); }
.wk-planned__list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.wk-planned__list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.wk-planned__list a { display: inline-flex; align-items: baseline; gap: 6px; padding: 5px 10px; border-radius: 999px; border: 1px solid var(--border); background: var(--surface-2); color: var(--fg); font: 600 .74rem var(--font-body); text-decoration: none; transition: border-color .25s var(--ease), transform .25s var(--ease); }
.wk-planned__list a { display: inline-flex; align-items: baseline; gap: 6px; padding: 5px 10px; border-radius: 999px; border: 1px solid var(--border); background: var(--surface-2); color: var(--fg); font: 600 .74rem var(--font-body); text-decoration: none; transition: border-color .25s var(--ease), transform .25s var(--ease); }
@@ -7,7 +7,7 @@
@@ -7,7 +7,7 @@
"order": 6,
"order": 6,
"bullets": [
"bullets": [
"Built analytics platforms tracking 200,000+ live game servers: ingestion, search, ranking, dashboards and licensed partner APIs. Ranked first on Google for several major titles.",
"Built analytics platforms tracking 200,000+ live game servers: ingestion, search, ranking, dashboards and licensed partner APIs. Ranked first on Google for several major titles.",
- "Self-built and collocated 1U and 2U servers in datacenters; designed for scale and DDoS resilience and ran the whole LLC, from licenses and taxes to partner relations.",
+ "Self-built and colocated 1U and 2U servers in datacenters; designed for scale and DDoS resilience and ran the whole LLC, from licenses and taxes to partner relations.",
"Rebuilding the idea in 2026 as GameServerLists (launching soon): a 14-service, game-agnostic server-discovery platform with multi-region probes, time-weighted ranking, an anti-fraud integrity engine and a tiered public API."
"Rebuilding the idea in 2026 as GameServerLists (launching soon): a 14-service, game-agnostic server-discovery platform with multi-region probes, time-weighted ranking, an anti-fraud integrity engine and a tiered public API."
],
],
"stack": [
"stack": [
@@ -36,11 +36,11 @@
@@ -36,11 +36,11 @@
"githubDisplay": "github.com/OpenVibers",
"githubDisplay": "github.com/OpenVibers",
"linkedin": "https://www.linkedin.com/in/fris/",
"linkedin": "https://www.linkedin.com/in/fris/",
"linkedinDisplay": "linkedin.com/in/fris",
"linkedinDisplay": "linkedin.com/in/fris",
- "resumePdf": "/Alex_Frison_Resume_Oct_2026_68186113.pdf",
+ "resumePdf": "/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf",
"availability": "Open to pretty much anything — engineering, platform, analytics, ops, sales, whatever fits · remote or Pacific Northwest",
"availability": "Open to pretty much anything — engineering, platform, analytics, ops, sales, whatever fits · remote or Pacific Northwest",
"status": {
"status": {
"now": "Contract software engineer at PowerChat, building its developer platform and payments program.",
"now": "Contract software engineer at PowerChat, building its developer platform and payments program.",
- "also": "Founder and sole engineer of OpenVibe: seven services, 20+ domains, one operator."
+ "also": "Founder and sole engineer of OpenVibe: 39 live sites behind one login, one operator."
},
},
"pitch": "I'm a software engineer and business owner who builds complete products and keeps them running: websites, platforms, payment systems, AI agents and the automation behind them. Over the last decade I've shipped 25+ sites and services, rebuilt a live payments program across five processors, led a sales team, and turned an Amazon data-analysis job into tooling other Amazon associates used every day. Give me a problem or a rough idea and I'll deliver the whole thing: design, code, deployment and support.",
"pitch": "I'm a software engineer and business owner who builds complete products and keeps them running: websites, platforms, payment systems, AI agents and the automation behind them. Over the last decade I've shipped 25+ sites and services, rebuilt a live payments program across five processors, led a sales team, and turned an Amazon data-analysis job into tooling other Amazon associates used every day. Give me a problem or a rough idea and I'll deliver the whole thing: design, code, deployment and support.",
"seeking": [
"seeking": [
@@ -68,18 +68,42 @@
@@ -68,18 +68,42 @@
{ "key": "tools", "name": "OpenVibe.Tools", "url": "https://openvibe.tools", "role": "Tools suite", "blurb": "169 server-rendered tools across 8 families, a host registry with custom-domain mirrors, and full SEO plumbing.", "stat": "169 tools · 8 families", "image": "/assets/img/sites/tools.webp" },
{ "key": "tools", "name": "OpenVibe.Tools", "url": "https://openvibe.tools", "role": "Tools suite", "blurb": "169 server-rendered tools across 8 families, a host registry with custom-domain mirrors, and full SEO plumbing.", "stat": "169 tools · 8 families", "image": "/assets/img/sites/tools.webp" },
{ "key": "games", "name": "OpenVibe.Games", "url": "https://openvibe.games", "role": "Scraplandia", "blurb": "Browser multiplayer survival game: authoritative server, client prediction, Havok physics, physgun building.", "stat": "56k LOC TypeScript", "image": "/assets/img/sites/games.webp" },
{ "key": "games", "name": "OpenVibe.Games", "url": "https://openvibe.games", "role": "Scraplandia", "blurb": "Browser multiplayer survival game: authoritative server, client prediction, Havok physics, physgun building.", "stat": "56k LOC TypeScript", "image": "/assets/img/sites/games.webp" },
{ "key": "community", "name": "OpenVibe.Community", "url": "https://openvibe.community", "role": "Pastes & posts", "blurb": "Server-rendered paste hub with forks, search, feeds and silent SSO, proxied to Live so identity stays consistent.", "stat": "No DB of its own", "image": "/assets/img/sites/community.webp" },
{ "key": "community", "name": "OpenVibe.Community", "url": "https://openvibe.community", "role": "Pastes & posts", "blurb": "Server-rendered paste hub with forks, search, feeds and silent SSO, proxied to Live so identity stays consistent.", "stat": "No DB of its own", "image": "/assets/img/sites/community.webp" },
- { "key": "sites", "name": "OpenVibe.Sites", "url": "https://openvibe.chat", "role": "13 front doors", "blurb": "One catalog and one template generate the pages and nginx configs for openvibe.chat, .codes, .wiki, .news and more.", "stat": "13 domains · 1 build", "image": null }
+ { "key": "sites", "name": "{more} more live sites", "url": "https://openvibe.network", "role": "The rest of the network", "blurb": "Chat, tips, memberships, video, blogs, wikis, cited news, restreaming, job and home search, robot control, coding agents and a developer platform, all on the same login.", "stat": "{total} live sites · 1 login", "image": null }
],
],
- "networkPlanned": [
- { "name": "OpenRestream", "url": "https://openre.stream", "role": "Restreaming" },
- { "name": "OpenVibe.Tips", "url": "https://openvibe.tips", "role": "Creator tips" },
- { "name": "OpenVibe.VIP", "url": "https://openvibe.vip", "role": "Memberships" },
- { "name": "OpenVibe.Host", "url": "https://openvibe.host", "role": "Hosting" },
- { "name": "OpenVibe.News", "url": "https://openvibe.news", "role": "News" },
- { "name": "OpenVibe.Reviews", "url": "https://openvibe.reviews", "role": "Reviews" },
- { "name": "OpenVibe.Deals", "url": "https://openvibe.deals", "role": "Deals" },
- { "name": "OpenVibe.Coupons", "url": "https://openvibe.coupons", "role": "Coupons" },
- { "name": "OpenVibe.Trade", "url": "https://openvibe.trade", "role": "Markets" }
+ "networkLive": [
+ { "name": "OpenRestream", "url": "https://openre.stream", "role": "Restreaming", "group": "people" },
+ { "name": "OpenVibe.Chat", "url": "https://openvibe.chat", "role": "Global chat", "group": "people" },
+ { "name": "OpenVibe.Tips", "url": "https://openvibe.tips", "role": "Creator tips", "group": "people" },
+ { "name": "OpenVibe.VIP", "url": "https://openvibe.vip", "role": "Memberships", "group": "people" },
+ { "name": "OpenVibe.Video", "url": "https://openvibe.video", "role": "Video", "group": "people" },
+ { "name": "OpenVibe.Pics", "url": "https://openvibe.pics", "role": "Pictures", "group": "people" },
+ { "name": "OpenVibe.Download", "url": "https://openvibe.download", "role": "File sharing", "group": "people" },
+ { "name": "OpenVibe.Blog", "url": "https://openvibe.blog", "role": "Blogs", "group": "people" },
+ { "name": "OpenVibe.Wiki", "url": "https://openvibe.wiki", "role": "Wikis", "group": "people" },
+ { "name": "OpenVibe.News", "url": "https://openvibe.news", "role": "Cited news", "group": "people" },
+ { "name": "OpenVibe.Reviews", "url": "https://openvibe.reviews", "role": "Sourced reviews", "group": "people" },
+ { "name": "OpenVibe.Help", "url": "https://openvibe.help", "role": "Help and support", "group": "people" },
+ { "name": "OpenVibe.Quest", "url": "https://openvibe.quest", "role": "Quests", "group": "people" },
+ { "name": "OpenVibe.Play", "url": "https://play.openvibe.network", "role": "Achievements", "group": "people" },
+ { "name": "OpenVibe.Inventory", "url": "https://inventory.openvibe.network", "role": "Items", "group": "people" },
+ { "name": "OpenVibe.Deals", "url": "https://openvibe.deals", "role": "Deals", "group": "people" },
+ { "name": "OpenVibe.Coupons", "url": "https://openvibe.coupons", "role": "Coupons", "group": "people" },
+ { "name": "OpenVibe.Trade", "url": "https://openvibe.trade", "role": "Market data", "group": "people" },
+ { "name": "OpenVibe.Work", "url": "https://openvibe.work", "role": "Job search", "group": "people" },
+ { "name": "OpenVibe.Rent", "url": "https://openvibe.rent", "role": "Rentals", "group": "people" },
+ { "name": "OpenVibe.Homes", "url": "https://openvibe.homes", "role": "Home buying", "group": "people" },
+ { "name": "OpenVibe.Food", "url": "https://openvibe.food", "role": "Food and meals", "group": "people" },
+ { "name": "OpenVibe.Host", "url": "https://openvibe.host", "role": "Hosting compared", "group": "people" },
+ { "name": "OpenVibe.Services", "url": "https://openvibe.services", "role": "Developer platform", "group": "builders" },
+ { "name": "OpenVibe.AI", "url": "https://ai.openvibe.services", "role": "AI layer", "group": "builders" },
+ { "name": "OpenVibe.Codes", "url": "https://openvibe.codes", "role": "Coding agents", "group": "builders" },
+ { "name": "OpenVibe.Actor", "url": "https://openvibe.actor", "role": "Task agents", "group": "builders" },
+ { "name": "OpenVibe.Bot", "url": "https://openvibe.bot", "role": "Robot control", "group": "builders" },
+ { "name": "OpenVibe.Run", "url": "https://openvibe.run", "role": "Jobs on nodes", "group": "builders" },
+ { "name": "OpenVibe.Events", "url": "https://openvibe.events", "role": "Event delivery", "group": "builders" },
+ { "name": "OpenVibe.Watch", "url": "https://openvibe.watch", "role": "Watches", "group": "builders" },
+ { "name": "OpenVibe.Space", "url": "https://openvibe.space", "role": "Static hosting", "group": "builders" },
+ { "name": "OpenVibe.Zone", "url": "https://openvibe.zone", "role": "S3 storage", "group": "builders" }
],
],
"education": "Snohomish High School, WA. AP Statistics, Calculus, English, Creative Writing and Spanish. Self-taught engineer since age 12: systems, infrastructure, graphics, hardware and whatever the problem needs.",
"education": "Snohomish High School, WA. AP Statistics, Calculus, English, Creative Writing and Spanish. Self-taught engineer since age 12: systems, infrastructure, graphics, hardware and whatever the problem needs.",
"capabilities": ["websites", "platforms", "APIs", "dashboards", "AI features", "automations", "identity systems", "payment systems", "real-time chat", "streaming infra", "whatever you need"]
"capabilities": ["websites", "platforms", "APIs", "dashboards", "AI features", "automations", "identity systems", "payment systems", "real-time chat", "streaming infra", "whatever you need"]
@@ -1,15 +1,15 @@
@@ -1,15 +1,15 @@
{
{
- "amber": "/Alex_Frison_Resume_Themed_amber_68186113.pdf",
- "blue": "/Alex_Frison_Resume_Oct_2026_68186113.pdf",
- "cyan": "/Alex_Frison_Resume_Themed_cyan_68186113.pdf",
- "default": "/Alex_Frison_Resume_Oct_2026_68186113.pdf",
- "green": "/Alex_Frison_Resume_Themed_green_68186113.pdf",
- "indigo": "/Alex_Frison_Resume_Themed_indigo_68186113.pdf",
- "lime": "/Alex_Frison_Resume_Themed_lime_68186113.pdf",
- "orange": "/Alex_Frison_Resume_Themed_orange_68186113.pdf",
- "pink": "/Alex_Frison_Resume_Themed_pink_68186113.pdf",
- "red": "/Alex_Frison_Resume_Themed_red_68186113.pdf",
- "teal": "/Alex_Frison_Resume_Themed_teal_68186113.pdf",
- "violet": "/Alex_Frison_Resume_Themed_violet_68186113.pdf",
- "yellow": "/Alex_Frison_Resume_Themed_yellow_68186113.pdf"
+ "amber": "/Alex_Frison_Resume_Themed_amber_38b37ff8.pdf",
+ "blue": "/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf",
+ "cyan": "/Alex_Frison_Resume_Themed_cyan_38b37ff8.pdf",
+ "default": "/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf",
+ "green": "/Alex_Frison_Resume_Themed_green_38b37ff8.pdf",
+ "indigo": "/Alex_Frison_Resume_Themed_indigo_38b37ff8.pdf",
+ "lime": "/Alex_Frison_Resume_Themed_lime_38b37ff8.pdf",
+ "orange": "/Alex_Frison_Resume_Themed_orange_38b37ff8.pdf",
+ "pink": "/Alex_Frison_Resume_Themed_pink_38b37ff8.pdf",
+ "red": "/Alex_Frison_Resume_Themed_red_38b37ff8.pdf",
+ "teal": "/Alex_Frison_Resume_Themed_teal_38b37ff8.pdf",
+ "violet": "/Alex_Frison_Resume_Themed_violet_38b37ff8.pdf",
+ "yellow": "/Alex_Frison_Resume_Themed_yellow_38b37ff8.pdf"
}
}