From af464c2af7c4a510c2611ef72fdca53a35afed36 Mon Sep 17 00:00:00 2001 From: Leonardo Cabeza Date: Thu, 13 Jun 2024 20:39:37 -0500 Subject: [PATCH 01/54] add: glances service --- public/svgs/glances.png | Bin 0 -> 50295 bytes templates/compose/glances.yaml | 31 +++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 public/svgs/glances.png create mode 100644 templates/compose/glances.yaml diff --git a/public/svgs/glances.png b/public/svgs/glances.png new file mode 100644 index 0000000000000000000000000000000000000000..31e1a09fbab1988e5b34c053f497603ea3224ac5 GIT binary patch literal 50295 zcmcG#g*soGQyIAy(ITx8Imq@FAu74>i+oUI+JH9!&P|j$0Bkhf8z5cn{s@k5df@hJB-Edo=t# zb8n9b<{PWWTiau_q%A?g{;-Sa2CKQAACGL&PVFIzbMwSW7Z33iMv4paMyzUk1&7!j zv*deK^Ca|E>{V&?j|r0B^#po))%+erHB%@Tna;mH(y~d67_vgU=0}P`S-X_jrL~Pe zP8=_wH-*K22pk-+qsZC3rWdEP2WDck`d<>n6U0%B(z_DPO$jeAUw|D=t8Vf#y zcW@kJb>4v!9KZWT`HwZ#9lVL<{7O+8YYmMa5B&k&CGQsqVuW5vz0h!<+?w|CrgfEf*|2H`YYnI9| zEd>Tfe7%b2tD1WO!}+!;kNzer3REk)Rz4rxjiFo`9)9%q(s!ox9=G+U^mLDv=g%_F z2m0I~=KsHbB4~_p!on6-qaG&+HJXW1bQbC}5o#1pewI=}k;agE^fv;RM5D0hVE`rS zYsQb>kV-6S;m(Di3Zul$&M(Tj*;_&s?A@85(5(7fg7WYND%lR;OmRvwI~(ZVg2SZ*@Ax4t9#WO!`zM%jl0^O*%4Gy5R;&>lc-imt{uuZ zlKzO{X&2KAc#s7{$IOrDA;(~U*C3(~xo+L;Dh4+gV+(wM>Dh}oT=l=GqGvXdU|~-2 zKOZ!7@u!QXv&1H>g1?QJlf`(8%ac5MKhZ$K5G^X2`GgXgFrM~1`gHW0#o{)aRJ*^N z)Df4tNO_-7?H|84eF9d}+poj^puk8an2JA>U;8(q#!^m97)4g`jhDE8&W;bGQVC}@ z-`cqU!yLt_BFqPuvFlk>emaVTp=P`$$#-h@Z^au4&7HsH?yM2Fzac0VU>NG%{JKsSVu5lii}rIR6>Xa(gNvI2fbK^aGPq1nOfnC+aLa z%|{&481BC`J;=)#p3qYJh6Kcip~Q!=E^mx`*FrmcoLEP1;6gMlMbLqEavC50+99Iz zng8{gMDSrI?McMH&kAKbw=J~Mvacf6{7^e-Pd*MC;G?+XG82V=BbpYkFHdJ-x-(yd zGNZo!ZcFP-OvIB+O0?Dgj%782JAL^+kN@cmvRadr88|4ZH7KdpA!o+aV9vQF%CqLm zd{Z-R{=$)qF%wgz6`?|_<1dEa8Z=ONXn&gFY%MPT-#-hZ+>TZ_92C~Apm>V;e@206 zQ^*C9K+4#AUJsOS9cG_$c$>c!`ENDe7NOLHmG~3s!JwZ1Aah6Y|8#mV_h3?52}~y~J(Kad zoH%zLoR@q-5J^PFf1@kKjpZfJ7Rve?C*DhAIkEro%vfi{zdOJ!uZ~+2ohX*Ovrv)R zY=uKgb_q9%=Y4-C&G&1KpMKH3{CA<8G_B^9gI9!FGo$$V zQuv5};V|(a-Rz0#D4x_31>8qR$^bXCjmFx_>OaU~=2>Dq`N}GPVNZ2Sc23wZM`6T` z4R*OM!&m+LCPH^XR}o^FVu-nSczQjdIa)Pf;a2FfHYzzoJ)>DBT0xfEv&t7X?D&jw zh{iPb%8+lc)-q|C5jb^^dx9B%;l_c_wZ4Ms1j+i(v2Mce(OWGEd#*=PL7a@L7ZYsE zf=BW9o4wsTzZoY40iR(tJ-@!`m0tR=?_DJ>;NL&L?e61_CqUR7ZWLGDMv@oc3XSuR z3fxelnhD*x81DzmlzS*OZaE{Wtim$a?xyZ|1`IP%f-;lt@{p<=QD24Fql?j%$xd@^ z0!$~}gpQm=uRs3YtsH%+`-P4>bf!gKva~hg#EFHN<&=j` zbrku4>$C#vLvylV4B4Ga#=O&|q@-*u*MOH`sNh0atI8c(h*H@NZ|9d++TusNV+2k9 zH&ljEl5y>AE-_)(pS1&A)DQbsus2`ar82ccK|VbrG(jAmiqHRi@-kE?mi=McOTmQSL1D-Br-Gz-g8oD^TN<($? zyKpi=y0hu;DW^NCc-B~k?I{78_xunx zSiZ&n&Hcfw{>o3iIYdq7)oX5KLaeLE%q^z@ZX@CH#G+LAb8Zd34^l5nE)Ut7S0)_p zBAG1fqsJ2hJZK8L<=I$_-==>@(n85Vm^6V@kbBOp(AWDG(o!B(6vKb8!)vnZ-az|uQ>_lX^o19;-?|T~HKs0!I^nh~x#0zJ-oYIw z^QgeNi(VrKJ*7E&K2TfX1~p57WMuH!6s-1*rsinh6FXYCJF60_FfLj-lXG;6jjWX8 zgRVNg0X%cAuU;ODJp{TsL~_W49#Y~C!|TovlLzmYA+A?Tr{wp8Ym6rayRK@7hE&h4 zeaY|L%dM%QB9ytvC~kV%fZ?}wP2(z6H&GV0(4`rlc@ZiyF6yyztoqOADn!#t8V_XK z<}FYiGl525l$5;|y7lGY19+qTa3ec!lF9I3&C;YL3VoWFK zjUPOS$Rwz;?Xwo!uM$WI9LC|^C+iq$ARtH#Grq!+P^@UGtU9BLgGF>+)vB<*mwkw8 zq!qLA`!}(tm)BDm3>zMd+zv6OT6cW(BoOT|f9}h|;!}&zvS-8noiE85_?bP|Rkgtwxj5MRdT;Yk?>)M#FFn!^%f;&wOS57m_MhFI-P|Q;lQ1@B z>gnlGQBy;WJ^HI}L;W`*zeTb^Jf5{F)+kSZ&2UnrQt0Pv6+}5!yhY&34^E-hWd||b zxg!nl+ZP_{Jfij+6GYO?*qA|H*Nq5f(r_aqBdFDgxXShcH#heaPdG>Va+10eD*n^o zlrtza-jkWEgm3TXN@oh2s7^1Iew%7}svZuKvU>k)nLCsJE!v#Cnf7zUUfEN>fpzDc zf&wf=-C*+%TbgVY?8e5%Fezq-T0IBhbhdAeOqg$%l)Ih=hniZuV_ldA*?g2DbMt5A zO0O-mLv|Y4y2mePY0R_u<~W{h;f=?wNTQk5;L({+`lp3Pn`Q4#?B!7C_-JLrm=L&vHu^4rH-@Ndci`S8#!hm z)aDN?5$BwgsT#GgRk7dSv$nQYgSN4@#(Hsid3R=sz45~Zw*(~K4{s%C)tzCJKex9O zSt4R48%VDI#A?ysc3X4kUTPD9x*?~2D8MNuL$F8!sgIatFbhYF$eFi2F(nFP4s2=) zr*q30`nM_{-}{CJks7^{xY3+;)u_)AFiJ&sLnJ`ir1viA=vOmn{p&}xuZqBFGfR7+BhlXY4Nzt-R!)>$S%JR&PeIUY~6w+C>7Rn6EM}_Zpyi5goT+#pr^Mzn=pS`7ae#;N$ixdQH zDqH2vH#AD)2(N=0F>4)4g?HYi1W$nddYAvRx983o@(Eo6Ifd4t1%-_!fgPdxmGBC~ z8Z%S5a~uO@3o0X%D1{n{*WuEo5?&fZ#h9L>FH7+ziJ+>HrzSAdHi$zb?fO|W;D`cqv}=8Qm>6 zxUB+k8(x1`nh8SL`mmj{+;X~e3>KuX*Vf?h1%0DFcN$ePGcy*Vhg>y1hj|1_&;Qtl zp42Cbw#_0Uk|<7fF`;ih!~fF?)LPZRv}U_kW*ON;Ygm}Gz(8U@ATqmynQ?pT8n1PD4xk-ZJGf%|J)zKBBnTn5w82x%MsAwx(_N{fkS< zcT24Pu7yqcJOb1v_Tk|TA*c-#B9%{zXR%dgE6VT}VC^cjob1mioW4j9Y0j}i9#ij@ zY@8;nr1T$u6JAfSqr8J1q;Cv*hhrp6{V*KX=mkkzdl4{He~$FTMM#UH8h8f z=kICJrp5m6mLn<}8b<+{q5mZSTDou1%W{nI7UVV*CPh#$}fM4P^Y#O{z5H_DvSaJwx2 z2i#aTBc6M1;%|uX3sJ8mTVEM+Lu&dI+|youyS;S{gVy9BE5N zzose-%Assgpf|6O`=gRpZ{@o9;D6We4*nJ?_7>TR2FabgGxd-Q7#Zpc4S^feg_b#(h&YgzT5^i%I(%+N zx9@n@Xe`wqDxpVJ5)4&Rzyi9`qpH3amFx>71pGS$(AlgOXP=@_$-Cte7@_%-jkwg_ z;-L|$;3ivEhJy%I@^PFQ#TR}`-T5GWS_N81r?&&*n1Ho zlKet|uoOC(%|0J-bUDsjyz%CwY}2)GJP!C>(<~<^$79wLcddL?6+JU!fR2jNZc^^% zn~|9rV>yzacfeoNeB>@7FZHnV$s0{j_A0^5Mz7 zyMm$t({DcWBChof{JgGKi2HCmWX>@XPh4mZ>7X?Cq%%D|9aQbbo2wc6q)9CvU%wc{ z$a*_)KDCp*xG+8~O-%z%X75#Z(m$o=P603(CCnu~5a6r`yGQJQ z-feVq(IKu9ySXr&4K*+^t7oOSii&Az8AeDR7Q|d0wzaiQ)VXY)NgjyNCtdLD?y9=L zXp@9ptbWcqA2Zzu^YS{i{wY!qt;Jv&NcjwGj2Z* zZ2!4VN9un7hujD;j<#++X1`16)%HvSFWdxM{n^q2!{A%srs7pijGbM3?M-b_=r zyH*ip-r27AlP*TxLnfM!3vBU6o)x!Q4QywVOWaBp)BxrHBQD-Fw9U83h#kb@%t~II z)5m!s$QL*vTq_-Uxeh(}*@Ui1uE%6g?hh z5XIE+a988w>AJ<6NeaOU+ZAW{HH@(zf=NwGd|T&ur}xuA&eE}>om53rwfkx;#1OF% z$#!<)8b&V3zdWBe!TK{M--nL3RndEOAbo8!bz+Toq(E85*VosT|Jmpd<^votSbXPW z#AZo~9n}dlgohGW$$a_e?Q`140+|lQztmKOjfmLIv|zTD77D5`4F4h(+fc@|4K@5p z$qCAxq&;U~g2y5RBYjPa>0xOPnPbkbZ%pjm*Ne{Ek&`JK)XkRnOb`;K=zEWfE3~ew zs4|UVETDR#zaR1VMo`UG#@g}DDQbt>mwg$`1D?)XE-f!-fBEtz(@#rJuVFki5fclG z1UI;%^jFaUNi0@fUESG9t}`Lx84cEMsc!kf-s@cw1dOPj-s+2AWcZqN|Bvd%pK2qtO zZ-F3{F4*jZgqWdvi}$~kmoC@DU{G9ExEhS(HvRN&p?cPKwuw$sQqt@CnkkePHL({1 zv@(|F=8toRPMfMOThNFUmSOotWdtqqkOBJdpTARlx%8UfG|+06{1^$-jE2sE|*ZLF2ixy{e_I?Y}`cin1f zX~|%CJ&Vk`D%ZwOFv|vY3H)ayUr|z(#Qv{$Vi`h=n|V`eY25*KrQC$g=(3?(B0Q;b zZ8wGmj~%y(8n7(U|J(IL=`Qm5+T|+8^BK9%xNuTqso*dZeO49maozMgtHWuP$h;f>$12Zi|}nOQ;SYIjv) zwS41s)*mU%lY+*Jd;SF)$KeeP4KnAH#Y?qiE<;^5$QqIsN1Y1U2Rui8`8ph9PTOjA zi#+Oasw+PPXpIZ6*m8fam#DXXCU<{CVR(J@yVx-`VQ@95ZND|j7Ae3Si%nx;LW^P) z8^$NiOftgPPuVO|C)Ra}f!!Ym$(PD;Q5GNF>n1io+ zd!<%N68R0qK^Bv{a|BY7KS}hhe#?f?+pqm@r!z*6HJd5o!Q0b&{xjp@CPl0a{CGFS z_$s;@?cqS}6Fj`gIc)^{EQ-pffJg^@b^XO#Cq+^K7t@FcgEzcsTf*-*vF1Vs7!KaN z%}2l*3k?yKi@Z;eRplOsD@6DY;Hs(t8yi*vV}Cpq;SA_|AICR+G^Ngzj@>g^OLa6% zYoczkj9#qJN*-4${@`eWP>^VMCg1iitD9jO@$n;G^Y~jPf=fp%EBI-GOmBs?l_=jE zBcy$`B;)4B*V(z@6pjEr4uBoPG&UC#gmWYik%ve|b2#>HYuKu<<#^*B2Sgjcf^}|w z(L-l0F0werGfr(rIuj&kIp|pCCSCP@L>~*EjieM?gp2d-V=9uheH2u&C@|6kuEn<~T(Y@;H?X!or*7E!#arJEe-|w5HsP7% z$QwL09zOL00SS+n*#!?5>(dz^lwT!Ynp zc-dq)+sk~{mTQzMD=J9ssxQ-T83+&MOhSrOMsEf17fE_6gs**Xoq8+YR&p*@db%zK zxiWnqopWV$A$o}8j021!e3GR$Ddac(Nv=BEI`(hdtLP^MsDmg;pVzn#gdfj)sh5Pb zJB|*57_GK4fh)KSSDYVsy z6R7^qVF*up4z282>ue>TRS^q31)xA z?^qu{utyGp4k2Rahi97t!lob2``@LSeiga%_Mxfc$XmRK5Iucg_!&fp$rJbS7l$v& zr?sI>*xq*rv!Fw(&jH{F2qsw2v zA1kZy0poYvM<1-;Y!UBvMAyfBHB&1vc3}AUv=w34>mc{HL7ho1`F!)< zt%P)mDa#2f?wlF)IBNf0K6Pi{XrQjD8tr=`Tp`}B71o73raWujpZu3D7|@IE(BzusyrP zN$~%){*ypn%ubSF{?|}V?#5^TwDQZ_ujLe(_8HR?{6j@4`R5N8o_(bla{peuG5AtO zX1B1Y2&bG@!pFLeDI^*T>O+Sp^c(72JGQ^#-$mhHX>RV4u;67DNL?p-|FH%%7;a?x zwWWdG;51t&!6gA9)^ng1k<9G0xVN{*ov)NGoVM8k+fDA3J2*Vtr5vqJ+0gV3>i;*k zFtU?xV~}zb<9NzcW!$Z$)&JV}-PGp*OEH2ujfAA(7DD*7j-lb~_OrKr*sD%V3G2LQ zPzfMu1n~X`-JZyo_-~YIhvV3mkOUjl)s;BHne#!l{)@OE8cQDTrX;Ct0!BAzO1~qs zSqJ8w*JZNG??G{8)QbYxt*fb4G`AVu+)ot`86cPeOJ1s9(Z*?`fqi8dYwrt?%=no8 zkw~M*QU94&QZH7G@EoPVD>WO^*0^~UD=Aw%cW77@f1sY#fpQ&~$Jk@%{V9c%y>P(# zzS83*!zwB9lqe8#2Bp;)63=>-EUhU0yA(&RWI&({abl^Sm~i8;^fkE1#{?UfV*Tm2 ziV8CMW-K+R53e7VTN%H&zPZm_K~g_sBpp{`1Dzr^1ii%!@AbZj)1L^E!*T2+>rFFh z-+2S0tH#>0nCbdNr-ah7dT5L4@K_7QtYzOiAgp2i^s;mlz_~ZC?nCDc_$ zZ8}4}E9{+cdfp^U{7D(8f2|=ufd!*tW@J!+6RtMpd zXB~zG>M#t5Bpf=U9w#(gE4K|mZu_?Fp8CCl({bXccGBGoUs%lfp*0XDM9YoBYQ*13 zsYFzK?E6&T(9A+->;dB9RS8><(KqJdxqbJsN^+^>cL}e^?>!C8jh%Py+fi6`n4Fz8 zEJ7HUJnC_%)HoLEt?)HYc{FPw1J1qHL1DA!HzBvL7{ znGe#yS0VQyPz9W1*?}OS=cx19bUjszGrW_*dqK#^>NhgwW9m1R?08FAIk-urElumagusS@}NtNL6E8p8hNCXt2 z?^?mk2Y9^|87G(5&$R!)kN>@L(Hcb@D6 z_h9g+;tl0SgIzc2H*rrsf{dtUGZVrx4tbJs>}OZiPM>Ve(tFj5$3HXH(M(4Opus*C zKddj>nDr5xeLP|$rk7wwM=*yEnWMo7{`&Mv;Q2>_#Zqm8LBz)~-)n5B`AOLONUZiY zJH%E}**4=x4nJ5bWhEZ6L;2VR|3srU6j{d*FlwtaMhn0G5~ka69?h3xNf-sP5hgT1MkekN? zJot;O&^VeqN2HwL7ld1KzK61mj0}@5vnuT=6DP`c@6~Flu!QN!BWMa`cluUfl;0o` z!M(e)Q^JeCcnhWdgvVA6>;XHQ{Y*|X(!o5j zixro2EgCXHB)V|ZZ}&`nENJ=kGUge+DP5c8Ks^iD%mZ%bjbqQu19y>ka2G_FH=cwH z<)2$^5*_oDm1LU;DaQ$|GV~+k4P6?-r#20kEge={s;N(g7E`UYPVyFd6-OF~Thbxi z8g}fKc!N?mv;6)eFd;wa>FvFxX>sYkV*1L=d>|du^^Pd@!O$7tOT`)j+Lt^-Mj9g~ ztMt+$91sYTlodCWDG(^T2W>MmGY1N(>-V3%eGkrP`@w?;&j8}|k8j;oTtN7FRqu#SCi2?X93UhUF1C z#?S7Gi+#S=vUyDC-Z-<1tn%UWmwD!{+C+_wZl?-&#UzO)5u~2X_1H|H$9r&sitMIO z<_f33te=ce|8+GC8jf|s(8W@SaFspF$9-sVzi%K!kgf;;=nD*l=L1MNgsT$_>?_}k z7D-*kG@5>24GJWw|1j@7m>2LQk>zR1>ZA2RB~=k+GrD#r~+Fm^HGbc zN!@Tu-ZOP%BtAn)Wf`KX@=5TYu{W@8BPrWwS2jBNsDvS|tB*W8e&btUnPLvPLlXDL zq39RnE#U^Gr{@}j-GPZsn&fyy@W$KKTto@y$PsCH0?jr<6eRt+I^_m_<2T>_9BSxZB`HJMz>L*Q!ji*e`lJ|EWo$VrI%Kdo&GC8U`0noeN`KM z2k41Maz`(=(zE?`x28M!{p)nXPqAs(69oYzIeYQu6q)Ti;2?+N;J`^Kzs8}GxXx>O z6T*U*^I@V`Vxeul{@lAs*9Mt^X9P1nsb*#%LPs?;A9-jZ0UY)2`Z;IJ9Q!EZ+5nPw zK76m9@zJTa}yz@cy+9KNFO|dW@N=qS%N@poH9Fc`K#oAC5AUP)z@EY z3n+rxIT@Mstd7cC?dsZq>$AnrU3+zSIcG6ROFX9#OU<|G?_LuX=nAwmMCaReZ#Xl% z7CjW;gD5;nOvUZCRGmJ6G(WdV2TAov><)g~#20IapN36_7*tibn*BH1o{MgKt_C%;f0;j_miPDFa4cgDO-8}J~a^?S#oe|Nk5$@xMU zErYy9K514uW5LjlDcB&9)j*|fZ3^67%N_m3-oOMOU!W~EeVTFwILeQoE;xu{$rv$vN2a|Fgc zYEZAXrCfDBQ5Jw$w>^^>g&Zbzv00whrejS`N@>}&NanN zB+PJ}_&(ye`R}THZESWu-z6niQq~yt0)ktM9*C=rCq-eTZ!+}ETWr<}a{F>_qXfDp z6#*F#IBC50)1M?MVA1gt6v`bRtr>s2E5nGfX^Rkk7%JVo>X?p@_Wja=%MsOwr01mm z<0JKCF9zPKKLi-F{6|S*P-3bZ9lFL&ZL_P#)GV2uj%Ktik_auSon8Hr9W~@@qBC~; zc5!YW@J~$eMr_Ngd^Tj&eHEbVaq9Y+5X=P`8nHmogK~?l=ldCJJC7Y0FnOX`@)hzC ztqOdBIl z8WEm+oANp)CCF`qk&%(lE|2x% z;?eJ5*F+?>v3Yr%iCA;131-*ReEq*JFSAjei=)mBo!*)kPbU-N@$>Q?JRtd-5^^k7 zdpCpa|KLQDxir9ZD8MH|#JIX^sTNP8IcRY6&0UcjMD=&uOCgBG#w<=!SbB*RM7@Oo zXVmO&=40m5g6S8pmu$dWRH-z})F)XhVHZEN=zoFEmZ}eot3(XQ?szpofwVZt30YJi zZoq!LgoTAglACzMGD~q<)Yk<7$(PxY{}KSjMvh4bP9q8^_-3KHYi1{=_q#^)(9qB< z3haG+9xV$*Su?$(^`y7%;o>O~VjK#~g9k{1-W4UjLLeRozt5bBr;u>9e?KM3Oh+7Z zk1Zh{8;1w|WujM- zt`Ee;832=9ORX2gJY0ylON9ebN%KZ!CeF^^3D!pPG$jj`Yp-cSFAt;cl_jC)j7RQ!R$2wZ7Z%_2Ii zI}@5(1KoJuWgkHGXz)fZ#kb}Yi~DhrvhQ^tfeUCRRcFs@LR!mC1mtI38+Dz^3eLjy zv*a##y6_#G;QxT60Ps`(wn)d+lD2sXUtcj?JiJ+)Z@W!I0+FI@%*`KO=K28%5WQU2 z{jJT-RPu1CN$5)lnp}u+U(wgE-XC-yfU9&aE?>JbZxnz*l!!fkoxqoX6B zW}Fj!fYSbXWL-*Cl?3PmRW&rwL)&8eS!}gN#h=rD5LZs&TmR);lh$|{eq##_&nwIi z0@V3z>VQ>ikL`x@?Zg?U@(YJUsh?~RAV}iz3yyrjX(j=x3V0uafNti8CSyM}^I?p5osFki0 z%%)2biTQOfgl7dpuI`wZj0Ja5o-!4j6T#Ba$jb1d>pg|b?Q(INQ&GEoO9K>tNx30k za8zsC+Z`<}kOo=egBdzad3pJF^G&1MP=?xQfrd-a;HsCRRL-w6lMy~;=ywWbI$?;T zhMC#&OFK(2ez{-V^kbg5tutS*B;dc>*^N4i+55YzPkM#_gX==%71Bcn$Yl1P^E_b4 z&m$rdQci@SsAGQpr)XDsZ{>CA|Oy^ckmnt z!Jdm9b%|b$Iz7y{oWY*suz7Xl%1B2|{S&Bm!JU(Oo{MokcO|)#1n1HxhfXSrii%;yF%@)RWtD3{Jy?3`eULLoe4JHaOv@mgOL^*RLtgJ` zmygP7%+Y59WX3^3LAwo@{PbwYkDqH0;p5*aZtbdRC@3g^@^Uc#spe@SnuK&nf93eL zD>u5sGc5+Q=R4mjKGqzFB`-c^XbhH=^rJV2Jwe$^^|7IO3UNaxMWMj}MVJ5n>|~zwIoyZQdP`lf!(rV+abX zy7Us^UR82b4q~@qfR7y4%*8$OWiB9a*4Ecw068u@JG;|5fAQZX|AmExJP2kX4st_2 zowK8>5`(MF6n{oca{hTHRN_7!^Ah4ivjDCA?F6!A-nZEg(_b95+jO%RAN18AC#~)7 zDzBp{72zVLr>C*eQTN5T2hj&MuL`sL>4g7}o?q3Wxh8NMa~1naw1CGCXZo*ucl`1q%VZ$r3&0lmQ7M_dON z0FWahigVT;ftn1|molo$`x?)mKOY(y z0rI)f1rxE|#1(v!+T6y*>zX3<0>VmKz*428NpAoVVf05q|0$nOSFNC29e&!E2D4PP z*nJ{1>2QMl1T$kJBY)8VvX6<0rvrI@a*IJV-QPfqX{8xhv4bDQ(#ju+h=rsw#n_ z)6+^u97^2aGN4kp+Z}KlPFc@nP{M;mMMZ)B8Jyi~p~Y}BCFt?8gXE2APIGQU!_`~J z#m1IR?7pfrIB@~{nJ6IJYr4<&DYMbd z_5Zz8-cDNJ{@1f`qj%9N+^PNt29z*!$;n8-eoR4FgBAjezCh~aRM=3S5xb9#u35Cv zaDAUWhBdt$i{IK+0rn*H-fi;A-)Q~t6!$wc1NnAnCI|yelYXgiUulG$I2+7@L9~XL z>6F=~O24`mK5sD7iAMAkptI$IHN0Hq3s!b2hmMx;V-XfrI~(EzD|G_B5t2BPdTtY0PAp6uu1s~)z+O=J@J#% zKB73M@P9DTtw!bvQV0N#tqv(nK*0`Lqvxks>T=gwpg&fIPjh0JF=s5i4Y*U&KUi^S zNY0JC{SSyUaKe+yW_Ozvq;}rm$u~7Mi8;Hu9gcpvUjm49u}q@i;9$;|2*Lwq>ztvr zb5jp_dR3s=vZ4&5?hLQ3KUC}+xy-kuf5co}=E9PQ$SE$yd+r8kn0{3InCzbHBDFfj zen%MXAO>o{10C?tR^FSg?^ARavfH7gWs+_vHCRGdnk5?8p=}n{){A-bobBK%J4VQ= zah-%?odh6%V)NioeJIl<9-q->RcsJ(ssYe2N{da^*QNFK_13X(x+H|5I#D*$uic0g zf86s!skbdfyQN}4}a~@+`nIIvhk;=jT0kpTP%>J z+s4f2#Ibwm^e5h)lt{t&$cQ?aHq+r3{33~dSq}UiUw4d}309(Sh_XYm)l~nmRfEs!vtET5ew8FP|nN((A*FcLzdY3w!(3iTopa5Kng@ z3Y_7b5q_XhWqY|keVWHh1Y*1+c412MsQ+`fvO7w|~~1Z*cu%p0Ee3sv_35wxrzL z1S4Nujv4@92Shw?0i4^r`LhJWYl#GOa_`-PS&H5HF8T~FK71Hu3t8NESLovFKZnHH z#^NT*?tC^h+oBKDLC}qtgG0n8ASh=HU&YunAK}|w-`HRWk1nFbcXqhKKJ*Vs+uI`= zT@Ixl4+G8$$KO6u&H*-EZVEsIrZzrxwd}*dZDjRfw8pQ>mU)M-n|FGOy9W%Yy(C7{ zd?eVp5>J6VM{zFiy8VYT@}-y8b8yQ#_z{uW*-Upv2ddw)T$c{3fOIL%0yfgco`dww8&2QE2epU-Y@MA0J zc4KGfMx+-U+IK@KM|UNzYNl^w=iXXcTCrhYjqv33{K3;8b5sR!d!Ms*cI+CC$8;N- zh4!sL?G^`8EwA1RtGmZl@*0Kja-oFSr=UpsXz3|#H?{u740S1=$GW(AMt0O(Cihz0UTNN6Z{uBO|Iz>*UM_y4kEQYNY1rk2L> z>(cQ{;^G(Kb8m2XomY(}LamiD45Mxjnh+6YI@P>sj~=D%Ot8VlKK-8-pxjhp{?E2+ zZebzzol1(8)xR4HdAPU7Q)M^ZgF50{4TkgvplG%%?q>nvk@PFQZ!wsAS+l!_9L%!^ zfRiX^2ZvC`6uSEMv|3DjMi~<|Kpsl_HAdzK4(!tVwV@QKj-j7Ekpc(^+Bm;V;~=!l zk$(@MxVSJ;;pc$Tf0xVTa_aB_I^FwXnG%E87zb57@+)vuwFN(@e+9N45hz9|d;a(b2K1*EDOGZ+Y;7BjsakEQt-WiU8QA5P=9Z-Ybtp z7uJ<}4cH4o%E%``*J)?^Dh!1mi_Jlnkjs@*yxB;PNLD&`$ZRgDz;S$h91EQC!18&u z*Q;5=yv6NWT+k@2uNw!jaRs){d2wCtuDg;(;GxxHo*ROxwKd`*%a|$#=w8HFm~%Oa zqA{w6@%h2?ims6QJDYMtTZFZU=T`>M$@4&^{-+t8r#4KKZGbkJbAZjPGtpUduNx>m zy2~;ad_BzSQ()WKb#*C;*Pn}Jr0CU0Wu%w5>0=6lSh9VLiL5YR-;rzuFvit^eCH`5 z+39YF6#rwUz#u|li_FtcAK*Y@+Q!DB0Hf^$Ie4~qYp~$@v&kpK7+N71qbVMGka(cC zELPb9Qo!4Ln)Yed+aR$eMKlwkYAtvh{o%3?5F)CL-o7ag7jNTPpjQfNWi*VIdnHu6 zvANj>unPBOLDm)YwrGPB0urMn%M^Ra0%I@Am}*c7G=cmi^Bg}xZCDIUp-a#B{9L`G zx$|fhp#)}{#*x49fjrc=IWsuxOI0lhtfP~YyTb>E$0>Ip1ku)BHe&!hQ}I;^*gq}o zYq_o0x87w>7f@||I4J-Sx?C?XrhQNAefwSVY5qMj!0uxBC)+$tHU=W+unrSWbe8E` z^WB-nCB)|CezVwWU*EvMmXEn$>idTeQC_?tCT%G_HZK;xuKF=)g=9P_7#&N&)wcB$ z|7N?bm3539d)=(b6y7QSLRywmng+drv0GYF*56-(CqVB+_%t~^{Zc6NbT?+J39 z!~GxJh+bZ>&*#jXnK^UL<2=rp+CPu$%lXPJ)*I5Jx+QUYcmJfGZ1ks2cnxl-u*PwN z^fX{jvFlb5NM=OIYU}jHsrce95)-xqNFYS+OLwdaS4dAe^lyVPRr9D$bITGMLEb7O zbfa!R4}a}Z*_Xqs^@D>0dW7P^8!Lj5YTeNIU#qK(&b72ai31CyZ{~XNyADhP@*mxW4i*Ib&E`iH#=9r)%>KgwIZ%a-F|v1?64EFBEKgWr;t0murOqnPYpt8 z$@yFmDx4O?G<{96+_QDnwK}F8`Yq&{kIk!BeNyPZ7ekl5rfezh-o49?DWV~n(LMa- zIS+7URq{JN@K;g9XF8SQR5G+D*EkTWVH{HXiV6z4ehcvmdGhj6YS)K@11E|P)K4GB z$bj$X&v5{>BOG18Q#K67tTBJHcyaYUv(K*E&N6ADX%HCYF_*EkRAix;o?r8p=1tn6LJfox+qMk0BKI z*Cs!25`pNg>(EX&*40v}c3&yq{NWA3iJcRk^kZdF*D8nab{DFP9H~Jhp)hmvX)*!U zVu8=SjNp5EdYS~76VuZ_+Jq@_8u&6*im7zYJH1tBOzH*ghtf7_+Lrb`NZ)e}k!+O9hub_a+`>C!@s8<2xCX9kU$uhP(we}>mK(uS_#0A^g1F6-g;C$yr zj%FP6J2t_fXno(caGi@)@O2Y_yZJp+7&%*MtCaHpd$qTVlj{M3!4kviVgl<(>t z3AR#cD%p^dc!&}62ZL319V=k=f4eIsYo9J~69C7qqPiM5j2Jc@jDIZ}Zv77)%t*=gK}Eg$B| zYUi~dv}~;ic;(1WlMn^thI@N01?Q}K;qYWt1Pt2xlw{yhk-|w)WMpJ^W#v8aEby-_ z?d;Z+5VA^ei7u45>}u_uBnU%?$P^lZC(gMGPZu10_CZ(Q(?{YosZpki zq1KdIxk6ZpwV9scmR44N3wyzUnm)Ag?A5FAN8R5QaCTMl`OE8qt&;g9I@HtIU8_~X z96*py%@nQrfn)WMxRqhQyZE(K#kl5yn&UMVf0DfDO2pub3^x_*am2yoKnl~3y)#|f{*Yo9k?4MKb9W}2x;Me4Ss?g?Vqr1RfT0%rl0MF8m>8?eZ7&Qx!sUp zFQdTVk&*JmcHg?@<{Ra7EHKVzcEfrk^ArgWDpUXQh=DW^@@T2msYTln%0%Si$;I&0 zl*l&SZ{U9cEQspiLyJeGY5N{yI_=-t$-`xzOOybZ&r34u9-D?5KZ`uf-W(rn^N6#L z(k&nQJbcKVa~q~Ev4(UvULjRn<^+E5zeX+j!ouyMi?r&Bic-H2>Yk1cOdTB^peZQM zzbu)Qx#O_)fSvm|XbnS8Fxc>u1VgF^a6An6Yp?9 z`x4f%DmM+*V+G=cs3*XtO zIxb+Y-hJ}oib)ZDqfmWs^Pk;i=ina!0t{%I?BMp*@!COu(S%4N=I5b}LU-(1yBX#< zINwbrJN}yU{lKSL>kmKW9fK*=DAH0sz=4yV;ULKsF~Lf};Rxz>)PAatz=8lJ1yV>j z#PL&$)mRx~detN*K^F!^H}kXK!dU>ET^AYc@(4+e?gs&m6)7%ef#mT7|LcmXs=;f6 zd;z>b^sr87Sm(05<=PhEpam5+z(j}V8uRoUeW~#Rci#vA1#W*wJ;l*5H%j83iX=T0 zTM^yj6(gCtCws|6I52*JI#X|~9$7iy&KwU}*aN&sO3+e*t#F_*6@bxl3DH={@p?86 z`3(P9xx|CaH$OkLQ?I5@aryD8on|Gt69FzbH^W(ip2yaEC}Ej|h5+wmzK^eE$&Tqm zXlSUcROHY`$|g_on4LRl@o83zT%pMf*cxBJ-)yJWBZ46S=HaODK ztQ_D62W=FfoycZ{!SfY?iB4;-4hGY7XDncQUMw!{1>-z{(LF_oMM`3#)+op5rT`pp z%dKy^x1X}}u+!OY>~n3*&ZX~Z^T zXR7o%5e>3uv?8Wkt0%cKKm8UfRf$<`!br53nzGXx=SzHie9(PYl(`HxS&hl-De!}s z$L_CBfbtsGdR* zt_FS}I+%iB>{OLV^8>z(lYaM@KA2qlI)79vP5*|_Sb4r`ow+txqLd^P7$n_j@z3ixHjtyPGe z=|b}YC$x7&_2GS`-d1@U+3I3mGklDKn1L#VH*v21`L(PHp9?3$ZHK~J~%0e1+SdO;x7whUs4K=|50;2t2 zd5UxG7ewb)3S4&HrdTBdpc8eD10_RGl6SN%mKA$mWTq_y0vvAuTCr;Tqx3Iy*P0hw z%|fi~1gu=J>G}KX5-W**T1>0z!5sfg@;rB_6lsL`##uqcWZ&5M`rbTJ_jFJzz%;rU zo%r~McS@ZzflC9fPq6qo|L3_iL_gwKC&lhUgH)zgPY#%W;NMz36i&*Hk8;J>EsKY! zOyX3y2}~acPzRxvOyC0*6(3Il6t9QRWIr>q33$L8z*bcC3I+FuQQT` zYCoY+ckXL|&v3MD92x^@STu|!4{bbX9N&C*YFK5fT}iji2h5$DpTEmXF3!(+A+Czv zO96{@ZQLKjE5{Q;dOB6_mKy+elUuO%4$eLY@b-0^yxzyI(PNK3?ze$>%)V>UPO~Vi zPqdqP!ASPvaf}5mmyVUR0D-A8U)Id@bSbd6`HmTN;{%tLmg@Rz3_czoA6t9x*JJ^^ zEF<}>0ffnEu(G>PmYIbrD#Ej zpMUlD+SeRln8->6scek^>1E7%%%_v+S;V(e-@kvSqM}0YYo*W8Xc&4)*MU0LdCH&t zK4D3Q9m#g?-QtY^VE_suH|mf=kEi^%zC{^9!C*tWSMZaKxuDDx;rIp%{8R*-!kN@T zKsEctP-tfK0VVv!;!lQFa3|4wlIXplr&)>z?2=PpZ@h)1C!QzsNJ`G{9k?5Kx53d9 z7EihTIk6tVDRle50a9q~8{D5E$!7&}#BDYv_c?haLi2ucyuc4^ z`+xz`*sW4>LgUyp2W_5eK%=Ea)9gd0HH}S186bNrUlYz~sHj39?nYZSqN_B~FWwXb z*R1J<}>`9KH29jsREb~vbK%lwX0xw+;n0)vvS z9){a&!#!95i>zVd891=C8a)JnA-nD^Pw2$3@BVYXDUV~fGQ=wrMCCqP96}dXq7+#@ zt+uvzH=CurPD`7Mi+9u^oNDmmah($IFSa8Q;vS?9t%7}7GXbq5w}~jchZH$VT6{zb z2?kc2Vd6B2A3&cKo_vUPo^s8Nq4l4x0C8{N!Lr5`{P=TxHz7?&nqHclN_>0z>tRy9 z@3Ir1XG(wN5xqMzGefC+Qvw1!QvG~&uH)bIbs_t!qZ36rU3IQBLBqwB_E_9}*s?Zr zW>i6h^n6E}_eslOj5`PJ*%77WR6LoVW2E$HSh1WY9#jD0bqauep7K)%nrW-+dgT8q zeg@t@M5UywqvN<+yD?}pph?==)AQn)d{}Z-&yH0&)cMiSZ={e*y#$U?z!<>53-}(q z9hOspAS%*~%Y~wWPYe*CF!RBT2s3S9Gvm*TiBl0gWf8E6$_1LUK)rCikwJ!^y12J& z><@JDfZf`Ph4jn>XnWxyTTm#}g=bi9((WzEGDWqyJw?76-jZWY&bHsWb1fE*+6X_e zk`?gdf#0ZAX+73Z9nx=J4&bv$gO4|A;k2GQ==-wiQ!q8V*~Nxobk=v1cHQDu8%m#f zX_}*lbV)x(L>SxJ7OjVmZ8*ohW|ce=tOi}$8Y|p*ppe_0R)9^z*whnv=2m(V3;1hy zg7TsEV&;DI#>mN2!XH(3AcDXUSD;9vL9L{i$~^k(*9Vk3zNnIffXXUdmU9}pi9^oX z7N?5`_tKYO09yXpX)+nn{lG%n?nS2wX)y7_Z`+Ce&b4~Oq6PouxC5OvkSuVxD}sg4 zV%KouHh^<*aNvs$s(w5=I+ExrPepaZ|7OMJWsA4XaAzNF@U-RkDfUP@37v!Ni*RKt ztv{Qa+CS~QA;5?Sav+?N3!f5!v$#x*Ca1xG@B1y69#Qj+Qoz0(r!pESY%m0lDULlj zVP*?_du*JU>}M=er%v%1T6I z-RJI=eD@ErD%Qk99;oi9)^cVlOpC~GvFpxfE&cmF{3UWm!phnCyLps^py2q=>SMnZ zzEigAPwamQhP=QNOcOY25Gtn)Jl#kQ&IG5`9#Y`uZg}VAD@(sdszh$G7!X9|{*Azl zQyKdE=Lt{@Cem2kI+3Mo556ifFYYh3GsmUYZY7y%-y2%bcm)g$#$U7>OHVb}gyZaH zo}c0YcS}vQc9>whlqSmaYsZ6oY0!kerO7hW$OV!7>2{*7?rs|A>bxoT9GuGL=8STR zP}Gih?e5y)fqbi%{umFW#j^B{WkOg##CkqV@o`KfjYU0G_@oT$m)HwId@+LKsc2|y z28DdTELZy)2!((lnp=c>49B5W_9tB)-S(JJ;;p`oz7v-;7=Vc%WPF}q7(=yDk5VGguTLF;2Scb1Y=Rt(-K#GW(d%* z@i4m?Vb?!eBOLjdM)PRuHgo(lN#o43M5TDQA~a-%&597kN#qG+Rd$t`b}O+8Y&@2k zvUN3R6{C(2G?SJZg9dT^3k!)Vp+b##3Ij`CbACB0cBN;FRuRD(90%AP}KD&8YABJPI5qzna6jg%W2p?gR{CU&k^NE-C0mhULZnwpxvmRNV5o-#iS zZePktqjJ&tQRMZR9QH=!f6q`iSZ*^e4n9B{aPnqqyke&)w7j1qr^Zaa{)Z_&HT9kB z3n_*-oa@~~OKicY{V1Q1^hE!@68`h4f9jtUa`lB6EURWNq04om?B;RUq_qJhs6x)j zS}t-W#oB|KsCYT$pR(zY@K1ef+>(+_dAf}e)ISt=B;=7Q%oKAjk{sp{x}>$SCG@xR zoWx0(7q4}yzQnDFz>9-3ef;>5f8gmQzNi~J{(<{tM`@rI`#P#Ad`%?zf3ueKjeIS4 z*5mik2J!bRHhL)4{rgUxD)6_V-76@}!q?T5gW|3dTG4ApvNSINQc#9>U@J0W#uQ@U zx>6?9jnIh(0k<4Og)Q5Paj-<%06Il>$IU*izJ~#*^PRpkYba_-UBJ=Ace_>;bgVum zd=W>Zpr?!Cvg{i&H=hv%8zR~I)kL7y8)DX26^C_gluqS;Lh6RChNwQHgr*EYsm(HT zS17~)VCIve&X}!pP>LC$c;C8Omx5ACB9pEyMbw}>W|5t}uMK30&U&Q(-2Ab|3-a2n zlH(*h%m74m1zc}Zjkm5TC-%n*=lz0056W3ue+hA2MX~DwR`VTKn^l%a6PlPmm#a*r zVx>N0$NtQ&5jfS$spfGx{K}lX=3flt9S-1}ILQEz43>iCl`@v9kGH;;pItYjKJh57 z{y~f9aebezfta_l=&|tF*yycqw-;eOTkR%$ZPX%shAWk-fyf-14Dzij<8O7BHr+t}eZ5 zRq_GDdmiIJMV#puF`~9!(YxAxT*ywB>ChIfkGgnU%pxZALs=Qm1ZNiPP?a@~qA?{g zq#-Jf5j7Q{LQ7LTWdBt)-Azo)&|gMj8DR1W_JvlwJ0|80-;q^^HwsdOH+~+w{d8YD z=;l=|rO_RWe2i{~8RtgTfhZzb$k>5dt(~e1omhu=VE7@@k9o?jty9Lj*Y51 zKXH;}n~#`}Hij+Vi4?vBv--|Mm`9wr6}h5>w%GfJOW&6W=n+{5j%#q@G&pbQ8rK`G~LKgxHc zT&u*jenpy{?ZK(bXh79XQt9MPl2@mD z&T>>u8;&Yh{w`Ts@CIb!?b@8l??N8C461jh0w^7hbpNZ`$=+Ac^-Trx^IPmWKyZU6}`)csHAKF)_ zfk7ZxM-Qqe7IW`PITWmf!k{2vCBOs){@|F;eEzjz?PodfAf>xH!^oyc^Hff z`l@JM0Mc-Q5-z+dMS~bplE&P73}zxhmHJ!rK(p8(`FGKYpWUBcFbea8ZLv~EjGGAe zI>5dRH(R)X-8r`Q7un{E!CX^|&BU@2jh#O1piQxSP#esWm=g*>UK9o*P6y&QfI%ot z4{2T)qC`{onjbUC1re^-Ama17h#PF(x=G*W>+Sm?mmNDUr&AsFTE7w{K7rqUEHALT z<+y+#eemIs)WZ_@PPS5?D~}@Wa%{S&U@r86sanlV7r?j#@a=E2Kvb7HWBtTt4g5c* zb*BH}BRpy7Tl?D(AD7G#vhFMbw!>d6>3btOkH|ttm$QagD8d&OIaPt!w@eHi_AeaI zY<(c^j?0uJ{NrS=Eog86#l6&ej1>UC8OIK@c3}k!X$oJ|SjU#L7O2BJuao~1*7-W} zCOR;0ufCB87*Oeh5)0fz)oQH9CMG6>z91`2tJ?knh-)fbRD=0p%Fknkebs2pgX|}P zAd!wn5|~YW&$uN!AIM~!7ewdXGx3R94@JUu6ev_`K0uwABeF3>rrrlm73bRIqJX6_ zorUxljH23RsEN&TLr=_;AASWG&wL!A4>YlrB|tKLPciNVj^o=+*PF zeOLBctt_R)iGW6UNKeiS#=pw9ot~SV)S3Ii3Q=bBlbE`S^!5R+N0Pkx`c3YWr-TFq z()oOf9_fjRd1C<-P@v*;*u}6QN^oyUpjV^RZ&z9EVS(=P(_SBqWp}Ou)d^V%C*^B^ z$pTbzP?XeKuu@Aa%`(|;rjmA{2)4=wD%ZJbTcO5$pJ_`_1MP(t%caR*mTrj>=h}~< zp?8>(AOc^V*?Ii7mbN=xWdNxH%sPRY)WO2oIyPFO8*H}MV1IF{jBe3D!iq2*eYY%_ z#fm5e=%U)UkCtcO4#_f;>+BDajU|VSI=7KSFbUdgp3o8*wprf=zU-@6@3HtA=Gi_t z#7fLR_z9g`|6vl{OttIIbh$!+=#GCk%Zf&*bv{vjtjqnN1AiE_EP3oPFea->j?^iZ zYA!~IO;yB9Wjxzaur{P!>$H!TB0S9n{hqQwL{wCUQ$jhbaCg-X4v25_YNUXpPj4}f z=Gh5`a6cUKUccqrDn_I`XB;+g>-y(a3+jP)7e&Z02=bOS`Kj$JIza+ye(&5@`(Mzu zz_Q#+@k7SQI60c*vnOPsIwx;P2j1_FIkt^futB``7F+!}iiCmo5)e6XZ^BcE7d61g z-3?p7mstrECzTORGk^5!bDA$O{Ekl59k#Z%>QrI!8fl>=d=!QX(I~8Ze+a zf=zU-9NfGaLk%@4)GJ!{wDMsNo!l?$k34BEb;WRguFZGUQ?}mmws)hWfRY+2-vX+4 z^y}j~i3MyDbEZf(_#L+{H<9c2*2N+sVYC(?FDBhI`$m*H!Y5Mw2{uP-Mt{+YSC6VxR{VD(_hs1EVhz=gJ7*2Y6c8+pt2M#s#B8Yqa0 zs>(`f;Lb2!Vi*OK11g*X81K&k0TM*(hNq|TGKO$KGWB$Djv8wvxx`gQ1jxc^AAe-+ zMHi3AVL@J3hcu+comK|KwKewQ91Nx}xOND$z2A+{QD8 zQ88c3;sj^1g&S~`^#MfcCbIa&XbmWP!nZqHv*v^){sVNgwcj3i8_peXlYqY1r9l|C ztSoJ)CF?$pX$9fy^`ll03kReLP>3B606?-h5^o{{V81tA8P-6$B;&Q=1P^43Zv4bXR#2n+4dkw!J~unc)|jfn;A;`tF! zb0EfCgD>-!Ni3X+R>Gqq+GE-G6^o0ZINhGnHfoV!)V-Yh8i#1idwVLgB}2;=IlgnE zZhBk+!ZM)D&r9EK-OCCs5W#*PMQ8VQ$IY;$^7^G{+OF~c9zF~!7DE&7gr^Qt&=^MS zN^4A9XWMFzXLDWWSedN-h(p}V8 z%J6GSe4cu+7=2~;3eMa|dwY9jhZ1-9_N45dWIES2#MWOa-3=Pe%F2?P4m%M?jy?j9 z^ZxgEqL`5Vg&(?47rcL0`kHAr;hWuiiWyI85c3@ybD3Uvdn5@Pi39eMAY@5@;ApQ?H+afFt0_>w_g<{9m(}IS0*@NK2Q$Iv{yHv=}9*2*)gz>Ps;MgT^v}z=y@ENwytfUb;v$9A_dam1on>@2uTL~Cu*a8my98{)?BSnKDO|{ zooF$VYSxTSJ!W!>OFlpq7zap8dM$0m){evMA-_*|1@7fV7W~u5IMkcv?(@3cf@VU0+~Wi zRhXz{os=^A+GC{2rbMV>8soSvL%AQm%(f(UpxL*8L%iF z!%+QR9BT?C5RY1)^JIn6`okl+MMOd!h4prV^>bs-0pLCtC;xZ?*p^`SdHoX>Xw~6! zE%5%Ks9#Zw!TG@(M~(Ood_lz~sl7dpguX-ST(}x>?;EmEST+Uml_01I6eagXh)&$S zG%yZjK2eq#M%tp4Cd4IX=;!0CSAM#A>Y)TU8iO^=H@0YTJujirVEMctgwy~&-FB}f z|9MIgKM-VX$KsejC`ExVs-0{2Qk%%&$&N%7piH_W>6UtfBv2 z5o$ZrwZHEu@Z|LXMSu`6{Y*e^d|pUmktk|9YeVf1uV~sq_TBlZubffV;H_!$lF*=b#{#}oSk!^u zmaTPa{RW3<)iC+2{_NOM4VtDq;Vi+Tv@q@UBaL*Fofk{Ey`n^A z%(Py@Xy(Q8*G@dm+h-o_7vQ&l`C{7I{0auhf#C{~IhEjedm%A2b}z_y7KCT9fwpq4 zg50j-)`*!A==qnTjy5fmrR_2qamllP4fd@d=UTlYKHp4RAT8jjS|zbPk_?op+dc>7 zL88zaT3)K5&gKADg9GGVNZTBE+H$x?;@Vr>p^3i)p>s#!Q??v0pgEKrNG@On6B}b&y?(ft2r76EGqBCxc|CnE4rDVo6|oqeTyw6 zKs{5nUG{HOuL1fOq-39TuN+jDq6!e?-}%#E(l>$Tl@OQL&n13QHgiF+8W2}6PNyjQ z>ecHN4j*e6;P8gROjt77Jc^H^fGIEt3_i&!)RkTW!sW8p=t|vMp+Ei{ibp*>^C6`7LXlro#Vd#k=uT;)Re3zd_et&=oO z{H4b$2pJq4l$D?WF9xhyAltN3JXs+G*YhvK4};p5j%@GW9{v8B_80dtGc1@`h3q@_ z)8aGew+FbkoN9oKzI#bFlb4@cAY(m1Ww0U8B#mrG_c#N}Zs8xc^C4>)DT1x3u=)>0 z_}=593}!ulPrAg&V(mU`Ir_3bs}jA2fi7wa^DMx=)V^sjaC(GXZh3BVG<(M3Uk)IE`ex`v3w zm7w7+3Uuo>R9>~JZ>b+X+#sO0aw$#3id^$S0!kX={Er+lDnrreHbQm|1v)rz-K%`hf$z+3QlA zxMUr1aEXf*s@<{5l`sxB^uHNJu%oMjeco%cclgJY#HCcD6&2&aO#W{aE`d)jq`o0C z%E-nNMzCh7>+y*z#3R3(aNQNiRb%aZFajhJTCklZH2;QW=7N*?V<~l|Q;YCNG2Pwv z8d6i9yiLF&2Du(j!2%*vpLkGm^;FABk1&s{P?_c~Yk0k`PuSeNg^_>b>s4FH3dNfFy+Z^W9!x!*rSFRpY zo80;9+d&c3JryY87&lb_p!ph8{nUB3JtFkwE@=oZKyBjxh)5;e+^dqc?|v0%x@*H7 zXpu2M7)WgT)A^`uiqJXv=uM~fsDez~e)b0LuP#Q7N$>rZ_Uun9fVm1twty1> z6gJ3kx(mv8fTjWfqu46B)Xwv9DG0*=K3N<8wr6PHruZ&=Hfry&Psj((y7IEyQW?o} zZ)Z*<-4A|uxghFd$MTA6qT9mLAN~GB@9{MTi=i73*?#Ztrn@BZ17a_r-ptPPHmjZ0Fwg> zho*`4Opr_?EaE9RTQLa?D%U9SPE`>nY1=!AB9RZSFAhFAaQ!Xl`Jgms#(O^&{;FKu zd3taWIMA0flY*CEq0^vl1^oTS&7kavlj#(bP1%?F!$$wJ)2bT-@okZwgM2kjILp_O zqn%E!-_YHcl8BU`W<)oyr!oG}d(=LxeYzE=dxN1QR8IEl3j#`ayxYEW?djC=mI~ci zIgb-iqCPx%rQSwCL#+6xHKN_H|0qMC&sP8{{Gw=)?vH_O9pniw@7$F!(r z)!U5UVg`#pvm|}!@%1iVSYTE%@VCLV@10DnAIO?2x=J58=Lxv5&3Mu8saHEPUsvrlStRv1}$|IZV*r z@_r+&dT}lruPy}m;Y&~MMlk@5^@$)4OtYHj&CqW1R;m028iWQkd(aw=UtC$!Uq@m` zW+Y>&9M7Q}1a543@4Xk0*7_x>(cRKS$hod%8I?c6s_fl(c(Cp19VCvqXrj@cB%dg} zU=T5(MVYDTRz|Njg+rG?yggAOwQ@qsO553hqP7MxTlELNN9On~t-DOa! zFzcpXjn@Zz@u^0~d)rB2Bf2iML+*1IVgs|{b}SDL@iz56%j^H3AauVN&lUW-5br6A zgYr-bDvn@n=Io%WqGzK4f;)2!(*{)DqbS_dst>@P_9vz}=yTz+sO>X)*}6 z4&KUeZ{s9i?L(}A9rpxob)dX~A{ktrtCy;iPg#42;Vw`(ES%%1K2$x?f}>E7T^mP$ ze;4Duc(f;WaCrOd*he(f1JsOPcjst-EuS$U`m`gumi;L=vE~ z=+EZPBy)P8=A3{E+*Dd^Zg6C#su!j1yZ4I@c0?DI8-w4>8&C2%|bR=b@l~={n!1k8}CmS zJMrp?`;f%od2d%@@x?C45y96l=9rJZd=u?$G@9TdCqq19?nNx&7IdV3Hx=D@|kFU53pMhB{h_d|1Bfa-%QkZ5c^4oB|?*zo$;F z9;>zTWjj<0A0nu_#kS(LLq@US4Z5D}m5Eah)Cg8BchO-_%Ahr$vM;j+8WH z?{xt`nRWy)eqm$(2D?l4j_#jdAkGOj{8^U*H{7+xWH39TG)|YE<6lk(Qye_F@dbay z4#qe+q?J_6VwL1xYBOhs6#aNqf|r_@lTUmGaJ1 z5dA;h<;aF`E2CDM5i-JRK3xo?ge>0WB4DQCUQDC*Te~+jN~Z1nOV{;2#lO+jM;sr% zvwk7KH)|KAgSQ^R@;TGTu&*5*+sB`wDnx(~gS3A6NgWMljbA4{G+97;cIgO9@^s+U z&7;2C%gG+-H*VAsvj%dss8LdqlEyKdo<_1&`}o~nHplecYR{!a^Ftb*)}S>>be}BY zGsCytL0ng{$V{E(d0&_XPmR;|7Y@lQY6$UnYS^m5-$dSs3GP&&)uNwy9sc5yZHw`~ zQH6O2jnP9-lxJM%k8{Mhn$2P>GwV{F*wKdD^bR|Y_U`p0pjCk9M^~Mpe-p$?W{){g zMGZn}clD!g#*o~9_RzJutx1$)JchYfx5NZ(J%f;$9>Ckl#`{yVwSGl6(?HO2qD2U` z7Q{P^Co+1}&cd-I@&b16Zl2At{zWx(%kb1MDkNDN?L+elnF>(YqnPtYr$-L{3^!Qx z9pKIWqM30{M~hv$>1HgJ`DNX~d3dgRm1}eviiV6W#Hcs(t5*LjF%o9NNsEE0381?S z%-cvYD`nu27|De6EV;h|#D>t>rl(3=myYBke6pXKxF$xk!nrKw}|*-Wll z_y0DD3E6rEAnBigV#mr6b55P{55uht@Qf%y!OmCDoANPg0+wQ_(ZCIxTdsD9I`h5C zi94ef5KL_nXhDqfvwc6YZ?hg!x3@a+7uxt7WWBBIp3Ffk7z>2t{X`FTjL`nHT**gULts~F6ScIs(k#%GUYmW@?fvtba&bLn}_Q`@^*@_ zN!7Vh3U&-;8a{ze+y|YYT?AsdcwlElgSi1_RV~ZVx>%4HWeDA_NoDm0@4a>D!ebve z%NZ3U<{m@#dsgN}K3Dp4E`LhG_}kkjvkU6iLy7etb2s5FI)h#eE*n3)@B6;HOP8pW z984fgpz{5mxdU9>_Y2|%D>DMTL^VLR_lc|hY0_<|!sbitXuW#bxX4G}Tas>ox|I}9gIW20MHfancO@G7ga;Ex`E?;@Pdj=lYLqs>BPNuWjs8n8V4 z^Rle4ymVvVuXqf9ar-|mKxBkaa|f!z1gSs+P_Gx}&+>im8etU5j5_*cV?BJ>;JiJ; zj)ijS-je3I?bIJfF>H-M3|c>sh+NBFB=YW`xBxLAL5$6BH5= zs<(q(+bUej_4hf+(GY~wNH%2aHQin1jH&mXY$ZTQ>TWWSl<;Q0$^g)?WNk;SYSyX zX@>SJpu6Kh{O>UoXrV!Gke!!*7S*e1%|%e%v5dZcGgP_Fj+87y5JP^0?yNsY7-M<$ zZj2b-M#M^?D4$Zztoi15Mm3I(&OC>bF9tA`0xSpde@mh@P39MgRXNNWn9HJGm)B+g ziRL4Qr-?%#!vy;2gZ*PEQpg)LuUBlQeYaZTzeYoC&(DH2k|mGmIJVmHgoE!foJ1$Y zwrPe{9gNkNOp{qipkCI)B6C#xS~70OH(2Gb48#H5Q$4?SjbY24%6TS`=G@0QrtW1q zBdU9=tEHl2g{&WlofBBCzvHh+l6sXhZt4EvU;^huwj869=3f17b!<-t+7>Yya4W4_ z3YT#Co2;n1>~39Ze3Z`td%fIvAw|I+@7!DhyT*iH2|~CEpAyC6>@Nf@YHYvw*X^%K zJpJFz@d9OTl0M!g6Qe1yd2Sf)62b;?sSNmep0bV!m}~Q`;Z+LX|LfS(z4>~Pubqb) zA+{z%tQ>x{FdsZ5whLszj2YE8s3=EKnESOfLaOZ`4x*!VufUI-zMkAM!I$^ui|&HE zjUN+0b->38Pj+v#O7(hf6nR!pAeVFqG>t%tB`oPV3&@GCU(XPV%~f{WBrFc4y}jHq z2(AQ4F9pfJL5#k0zr(%{A%@f}@Q+IsO8Jc-gKJ@E95-$rv=PW8c30`dU84;Q*!_narmNg zhO*D9E=_e*89Qn^V1oojiU!ScGw%K&J{Ykc5(xa$^zr>LJa=@sL^f7b7^{?Ejgx1x z+3nb@`&-WYE9X>{G^g-1(cFvCzoGve_#r&4tnB`aq>?hor$Z{*_e1#(3Tg^TPLOW2 z89LfvV@j~f>x=!|2ivDc{v28!O~ZD?{qzKDizP&yMPswKb;6sY^%n_hH`UJPWcXSl zH&si}9LqvH?jb-UObGfy)q4PQP-#J9u6ziWitD4yxZa!am1zI(p<%B(qd5`}B`5fI zH19Z~WsW~{q(dA+im(@rqZK{}e>M!w^G965Wg7EhJ%%~&5q;N0@w`GqtZ#j#y1aDNJTp;jS+HzP^G!HF;RuPcSU3PpM)BdryjTo;F@JgCao;b+XO*bJU5 z&TUYyW_@-E&1PUFgkTety-rKMCMtOX7U-wj z-%`wqSl7Mzxva`2c@~#Ym93|wwbCpuekujHi{hZ5GK!F#XHMsHx+Dog-}e!qphZGx z&l+T$CMYYX`FVblR8vP7DJe=`>Yb0G#DDD^d2m7Y;mA=ehyfEc0ZrKA+!GcMh!P6O zb!hB<=zDnrTcyKR20ti>Z6#9@uquA7;RhN*Eri?lQ2(MbX-xw1qzF zx!1@6iX5bY`Y3%>JDf;0X_h#i*X7Jmk#_FCpoTU`^AW9eTIu3tlY_wkBdCrUntl9d z>TMG$8Lv{orcr^V(g6GirU9``JC~&DDiO=Do56-y7B3xn7;94isSrY}AS)0kJfDa3 z{=Mh~!ghCUn=5DDB(Jk#2XC9`xF=u?fbeVa{Oa;wV{@zU1Xxfog7nq>D91JVZ+#Ml2F}mtRPz znf@CK?gx6@54fY$47eYX`Y#0`4&W+ad5jmfl-W$*Z%BuSYWo``&d(VUh>%^OuGthg zQX`}g5A_T^dSxAKGYcZw(!arXSb^8`mjOa#ynYypD?`H0*ELJHSOqH0%{`ex$CM!f z-+nrNAeCA`H1e$^sW&325zdLv_fQ^&gK^pc3ddeljJu(q#%ip(S*t?^}#YLpfc1EDFyPdA?4BW})wl}OvVa<8IOKpMmSTc^4JmxG2e47`k zyFHAM1mVcTujA)J!|A%Wp9~25YVs(rR`1(4C1x+G7NMl~T={T_^aBKb;@OqXwr=9t zUp3aF#6(%tAOqmp&_{VPB>cq0#8Q}x9uTO4n|{d#+3+&R)V}JVz3rzO*}Z!*18dGM zAQK_OV%>p6xlwJw=DQ|0gYKj3oK>-%qpl#h{`(1v^JFFrlz}x4+BdXEh0x z!u@c$Z76Q8;R9jj687JC^^6Ku*Yp8;z+XKf#glq@>OBzO+}nIlp}~u=YE? z`IHnE7M3+N`NZ?A3q!g|m6MZ*tCS(o^MTFW3`yp19RFML!=WSIiJ*4oF-Pl$q0zb& z`f2gT`zayliv{*_i=kbNQu=c=pb2szmN<@3#K^(ALC-Qbf!TY*TS1fNeF0?!-F|`P zl?EZ={F_TjHthDGyY@?M?faTh*L`sZLJ+#2?dbn;;yvV)%9nH|0U>giup${o7olbP zUl)`?LHi{FA|lt!5S;9g-^bhpB?`GCNzTROP+kc5(|;KUKs*s+Dj!71AQtzM`t>H^ z@~{6b_6Jy?Zt4Q2BRpF21d~+%Pu;G5oSMvVygwF%0s!wmf=Tk}IEyBb=ez$3|2oIs z&ak>n@3*91IvH4tzo1?P86iNSZ$uUFJJs=lC88@aRNv|{_a*c<0i>1a>G8z>JvAo% z5}K#8{S0zR4e7khe-3FFa+!NxnExxA1L6j$d4&6+n(y$QvoQRBl5zMP8A*imDA@0D z7yI(J?Y+Glugd55^d}l0krm!{9;~zl0@YlFMoa|F%Kt0wy&tLkon-TQdcQxP@4xW%gCC4@ z?sMPI=kqbH>v3HoIruAqb~_8Eo&WE54gh8kP=eXpx&r@ z2kx0@X$1X6o=Fh~(xLY_**n|)|89+23HK5rfgqs3ndSaW6{=lp@hmn!@&CEo7b&-i zKkC$^Z9`TthB@5}|*|VtZ;K1v}vtdjGrVH!l9WUVbWMMzO1D zFmzG{jsSy76L2Z_rB=ra)v!P`HIy<(9d#}l@dbZ}4R&;3=#3#=#6@5iO@V=yPm;}A z+s`xg1sBZ;>R`kon}VC`#ycv#ilDKA>+MGW^GdU0uVWa4fa1j9xrurxg+hj}hXVSi z@mHM*iD|#$x*B=1@FP+OMc2y;6%qNNUraa)h~ITph;(=-Jp8D4baYgDx+ED%FfbJh zq{C4K?GhkI72HjT7rKPgfR3R3oM3sJJT!eZg^cp>*!2HBJ&`uDvcVAZnu4Ul*Ru$n z&oWJhgk{Lj-Yn7LMFoyBRFK`esBr}OYUxw}=v|MGKDoBD=PzOVLRvX6C}?X%X^f%I z*T0a^{aCNGhHXbuI&4WT1Mkg8#IQ*P*ad|UXI+mE_b~Ssyqd$~?=T>@YBT3k2|JW; z-;ln<-wBPAMeJ|w+g)zpP#1u0^I!^uO0pnewp5%M@{C9f?7UxOoQO)B$iT15VO1%5 zOhm^xn;zl$omOd3e{o-wb}XtKN3jhDGUN)WJqmrjMJ9qso& z4f^kYkHF%7?f^J!l){$|Cp=59@Q><#KAs=5T8jF6pF0+Ki~KdS5HluDFG0VE-02a{ zUj~wqMrucYHT-N~UZtRInxf?|e2ajxZb(F0uwqth~M0 z5nOzKWyGfKu3NZj;nGKLBu~#zk_z|+2_wm0@4S=YR~OW_?_A*LaZft_kn+05p?^UW z^?>w&*71UNiEbVt_+qZHq~d@IN9?Bu;N*$WcK@0@2^P7WynH7|4-I6Px=}vG1T;B& zq)>M~N`1}(Wd!9H#-bg0_%Z6Yx2PuyX?+D?M~i;>-SO5m(HCc*5XPx}6>$H`S;U1m zDI{H1W5Au;Y|}4Uo0hODx`J-Jb4#_*fhTZZ!3heuKE9cL1x@W zS(5a!nWyJE=`ijzhMRbtYrA!22FO^4$>9X?>oQo?Bs?>%Oeyt~!>anLx#k}fbT|S|zi{!aYgq9Ub0d|E({}hDRP#@g8cQ55X;f$>_zL#*VLL@x_ zoEo$nwHBN@J^$4M?AzP02@$vn+VIOTBXnGuxj&h}A@MU6NynC_el~h}l63f#JH6{Y zxuc;pk24A_{`iU=x*Lq_v#?E+L8KRtD(C2yQ-OW}5R4G0QRq$-?rVT}S(aPY$9oYk zS!&YhC_dak%>Y4*B|rnTe`N3a&_K9B4Q8i@5}@*jdPmne$HRW5$eFft60EkhE zIeP)nKhdWz5V%Naw{0z$#j+t)$JRoXV%a(A`!}he0w9KoAOv@PqIY#c)TksN}_L@)!p=pgP*$YpNMR`X_I5;>Dk4^ugqUF@HPux=Q$NXdhd z`~Ew@P+@XYTjqs_X;QpCjM2+jc-!et3MB5eZXIj^VtOiprKZSp>+Es{2qjWA38|Rp z*Yw4e z5&s_Q+6Z3s@u*g#k6vcNb}P3=b$b$k*(w-_z-r|v1Gv#jXW}BMi zwEY{UA#8(LGNPN(*SYkcQNddOXSI5!mwFW}eQ7m_)kqX2L+R!D#_pGT36nLM8{6S0 zve+x3uc|ZWtIqU`K?XFw8PofQOFJy(FsHWm>3uA=tMh!H7%pq&vLSKW3?z;+WCwAB zd`iJM_HHifS0BNA2qRrS6atYNwMpM!ul>~5@=71q7;u2g@MI1L&=65p3< z%*6j$>V3RG{i-!9{nH+KEfw5#7O2ZqD6mLEyXZXTvy#)v>w)A8*DlhnAZE=svqWgc zOZiVv2R--J7niVIuXm0%#hp9`Hj9Dr9{A^t_0?=t)_X1%X68kQVJ4t_V+eE|+t2ls zv67G0gd8Q2Cng@@8bZ5!ASL&q{8ixo+Y9@%hAwKDlz?C4*XbNCJ=El>Z%i0zvf_^2 zq1N!dqx|pT=a6gV<|Rhd8pgjR^g%Ti|NQy$yqvue3$#U|{$7#`(7kSa_3G7?qz`2_ zL_aF18{AjV7y+c2nb{;r=&RxFYMg`HruB5@)qxCxK70#5@jhvvbVc*N?a$VyQpi}` z>gy!zHQlPXSx4=&jWlui zh?Ro~TV?E(ywM0)Ph1sgwO%#H#p1NrjQHwz1B}X3^29y#cUmP87L2cp>^a2uf{0j4 zX!U$dB^14%aI2UY;X}iq2z(#+d63=R!<%IdsLywELHsG{a9DWBuV$=wWZ_|Fsi=0R z`Z~6&l0n3w-W|Dckp0(ifxbJ#t?jB(aJVVdcY{ z!n{D)_qB5*PzLhoM#250ac*D^I49T$>OS?tOpLn*bOr~h)*)jnucmvvB9>9^UdRwu z>UJa#TTVWiHSs% zG^I78URR4CKT^!lI?;3BhPNYkpXuOuwTuJgwzS4~^-jtC0s-EF#ch3&^C0TvCKf zHWUB0s=z+>l7e_JAz7ySD&q@(w9nbJ_J1 zmvAc(CzZ7xDv_{9k0IsGg!)#TJ~E&~1S+9#P}2#C+V{=o+kkoxqzC1D?hN?LF1sHS zGyeBAMAOv6|H@Nu$lyc}{+}c1aR64<2cpJ>=YKrrz*=@~jhpK|!Ka-o_9pd*gQ{Zb zuHBA9X=AHtCHsPL9OBQ^Is-Hgn-Ip3p5KwJf2w9cEd!pHELj6$K4Y8^$5I1?jJsNa zY(}fUr{YBWR%A+vvY%7Vo@MEj^lO<8#V3YqI(f_a$^i-#Eh76sBQp-^HQ)FYBE=7M zDSUTU?wqBx@CZlXSo#hLs@P+~7bP_xFR$<$in)zMVs)SH-+j<#2XlOGr2yLLVEk9h zb>YH)(}q%6-AJ+(G-Ye>#|FOdB=;%gZetf;b<*{QhS@2!k>lzY{Q7-pPzOWOxV9af zC1@Nknli)tlS}M!rXXe@DOj#>oXp%n3WQ`)Q(j3!201dtK?6xVHxTg{sv^bxy>3wQ z^uCrk4_yHJk5Gy8t=7Hi`M$fL`OU?@7ve1CLHcD3vgY%4F#M z6*Wtx@%Rig2lXq?g$8GME+`B};c?=$OFQG2OKhd4j$~&7%d!J(KsHMG%bGiu(A3oA zS~_Ob820Z5Sf7s$eYs=J^mqu9=t%0GajD&DGWbAPdrefL7Ye?J!{?M{w$b(M;kVwR z?8O3&L8`wm>mmIE`l+SD)qf($)SEb5%=b9@))gx&QFb}p7w7#xDM)oQcrh2AB+!lf zm|eqv&u&KvF23E6O^VA>!VYDbA<{U3$8~tiGu+q+61hWP&jfe(>PN<9LEt!H1?rFR*;H-{ z^>^IlV8}t7Yw6Z$$_z#-0ulI03Md=YT@TPd6Ty>rH~8Er(4$qbc2vT>qFZX(>{gvh z<6G7LhqsETO4aj2>&J^6FO~CkF$5!TuwctMkI**mym9@Hnq6O>n6oZs`gRR*lQ|*F zr%zCSBkF59haRp4ugQ>O(%!rrjavhS+w)t)?I3R)r>MJb_(HHmnRvyp0kjwJYj!)t z0q$Ds&v)aFJ=HWdZ#E7y?y_uVB zt>~Rg7*cc2+&`DE@iI&c@4nr=Ip8!-ZUVz*K~SAVcL03CnjQaU;MEJY;DR6zse`7M z<~(uz#;^2UcG(`9ln+f$$07WOm*yK@IB|DXo3B(%XFpRJKM;XEVI0}%V4I*FDGnnY zx+Z;Sy^|Q|#9hGd_X(LxzvMF7(4ElF~B5n)qneB6~YJM>AV|VYLV|%P>1!#bnij8D}p3$X}R9get zanjnTE;X@&t;m{FU6m~?$x#5(A*hrRP_c6PAN3#@Hp^^~_&zFy;=KR(I4|jh*Wu6r)EnsD7 zzGxiKdW#SDQ8K7G&sVZ}zstXPs`fi#PLr|j%oncXR zOWKRQ|Aww>OW+mhwza{ZG+?6L^e@mZhzjX|xm1A!u{>Xij2318M|^v)DL&hlwH#FD zp|6iW-W%6_PZxc?Wag1~V;U!H6qYz(X+*^xeC8b|t(NpWdlsNf*F&mq`kUu5S0tw~ z3|Z=pwKLN#9!ZE3`FW9?MB~-Xy2*3BKGD~o8xUl#t%gn8=wj0Ex6(wRfy2^mCJxD+ zLWbz0d)qqtH~Ad;de2rMR;*Tmoj(j!nfUh#Q0MJ2TZ= z?2tp`6)U~Yq#c{T$!gl?X7mUyw2)So&axTzqW&(ub3eHuoIzkpoRG(MHND=K z#;#tIdPc6{Cb!I+3i!6tO-%?FP33Tl?*@NMpuWWUY0|^Z@dBI2z*W{Av_Vb9o8ZVo zA%S+~kZe1=FYg*JwEZ|ew2!P}7lmDKTz-1HKdW_hbv0tvhDO!aBivHzpglR%_=}R{ zB`X7ZO{RxVEw9t*cvw9ipP=B*{=gAhRJ#jk2TefC#IG1NHlzE5JI#B)UjN78{=l1Y zh?jHi2PZ0%m^n4xI30&q=hL$5d#ikbF52;l)ZV|M|HlPT4p{Z1e0$sJk!T1tao!<) zJ`E&4aTE_ZMTE8HkOrtSTRnG^80{VBrwt8Aio}rv)LmPlc`#32OH=nsbI&Nj1hvq{ z;lu*pkJ~z+#jA%0IKSKf=$7&Yug>vsw{D^}8y<~DQW9HRTMO})*1R%1(%M~K^ya}E zzgK{e3k$B=Hh{nCS+7aE<5V17GUA7y>lk-KkMqLy9DL)}fp_&@nDZ2aRmG)++gz8U zugco;D|&w@-$~(Mhzaz6MXWm)&N+Wnl!L&^j{(-stH%#9-?<{!fB=tX@uQcSOWwb6 z*-qrBq%nr(et#%H=No3!&-F@{-Z${Hp*o^}y&~-6+QG392uGQCcjuShnE0jr2oqSj zItKG4Vj#!$>Cw&0Y;NCXVxDksBSZXkD@9dYO}-CYG47T53r)@G>Wc|cZm%@j7*xG$ zHkxgbIQn&3$tMa1pj>kHup>hXtP@7j7h5FqMh!Wl&?QmBe)+jJ!{;9AdG6>{lt}-c zc_(jX5?uZ9#3qSO9Jvsp`3D^PmaXo;R;+D52l9&8e)8&#RSyK-KMKeJm4QIIAM6+s z2E8c(OpXRe)VDYI3b&&`XFi^l`qCBcPGT95oCuHao~9O;)CQ;gmxM=4mipgRF8}wp z6d*J|XhV$#If4BHdLmVTbwc67H_=k&M2S?8>2URYL_uz<`3d=-yp{HK)ST1=I5P6asvlwNXO_k(ZW`|W*^|0Q(DhPCgKg_ zf1f*GonRsF9ddMSoiu{Kod?^YGFUuf>dE6TN_k#BJg!(+xu$&L+uxYM6t6SB)VLcE zuO@1e;yX(bf(=(Icj=)i0~pFeZ&KpcpcmA2=>#hZ>sO>yO6z>XGQ}=UPGQ>|)Uep` zE=?s-q%dB$pD+WOMK^)vSQGO@74go#5v6y|Xl;+9dOd|$1omdr@M%CMBNFTrMKsuDcT~iqkosL!KQ}oWy|C~wyBxLV9?(VX6 z0Xg#1bQ>TAc5)H>ppSwZJB&qfPF`j2+0OE0N|a!n{57} z3<#4oBUanNrPk70&@}5I4Pw2K>4ww%`UTp3Mal z2^}EGw#~HsjnkV>pDOy2FDP=_6^WO+yILb#lS8zWd+G z^zR<8$i>qGIVvss*w&f@v+8XS@$93W`B5laX}u4}Q`87yHS*C^Y9Q{7iY?k)#lj`E|`Bi~jpL zk3i=<;dgPE>WLc^lvVeF8P`nF(h3xetEcxvft8SsMW?#!>TSuj&9?vo(V0&%9&>4! z3lCfBW)>X(8P25sQaj-atOoA1dG!fg^7odv7+>p&oc#gAHTinrw3ivt4#p@Play(D zyj*2J?RkPGHE}v>>p(vp_6jOY@4ldJmN0FP~k?l+s|$_eiqQL>1ynX97?~ zT}@+^@@g2YL{rKEyP70#N8)|X*ZMhXk;ggba}PP6$S@@elR%aUaG!3*9_f)c)YYiF z7+J!tZwJR5AWIL8R}c5X>P^CilTQ~tdb3+LfYx2H5+PY&%!@BKEqUK`X3xuXQ`+_9 zXvYZ8@%brmQ!zkvgDqnJOJz%tbclfVWM%v9QFmLvC`k~f6@kV$ujK~QT&~5SpX>{p z+!SNO(;q3w$z@Xc-b>CaxlH@a-H$~a$zrZ+Ib<5_`bUtxJvqyT5WVRCfQYBUU_x%ei}X*V0WrnQieC zjOTFXC>J8P0vWXX+Z*r2;GjH8bj6DG~B3-y9Emws}HcI-)}8O))2Kkha9BS5m|mFK2R-NLW<`m#vw2$`FMS+9e;-> zA@qFXW!TjD&hl>4Sx>Pi^v=WCe;zk?xL$DCyrI${Uh)bkw7yiqgS!!HE*|(E=s&W+ zOOW{Wdzs@K?=NFWz#%{&g)B&)$kC}3`x|o)G_Klf+BI_1jST==#M~p)zj2wMOndPL zstd8|hqX^LEL(cCe(SV-14Mky2z$QptGqohQW!goFDlIn^}=n(bSmCdoIM#`AVUs; zOK6zG%B8mitUZoYT!ciJn=(Vstc~VzQMOR^@SOs7$|(U9up=&C_Y0GE2G>Z02KpXhDE0g9 zP9mq&c|~BK(t&tR(^JZQ)DSO3=)ZtT5`tZx9h{~)Xpa{QniIJ0@lAP-wP(D)Zf^A< zE9JyTYv3@9g4WfW{~O5QpuZM89_lD zFVRXIh*x%4Js7viH^{%tSF^Wj*<;ghZ-51dMovAvoy`_LvWa3Ys4QIyTUGc1a6XIMvL7#2NT<<0|(SU%w zJtl0sBQM*a^7fDaW3*>?=)J(ep=8Kn?DV#RrIx(e20$HU$eT)roqT%`*h)oqNz%AC z)_O-6(41tpl;2Y^xKd-KxWa+L8%fsSNp2Af$|CXjXzdMpw;h_~>d zg3AI7d=$W509$y@IuV57tSlT}@wa3ivF+wcAe``;6_0}^}irV=2`2KwT#*DIhQ%%e&_@V@U`lvNvO?9j7Cl`MG4zx4)moNbCEpphj zXc~09_0uZh0v|EB5nnj~VKTYqeH^~s`j|MPUA2q&wE?%8Q5XgP|O{}N|7kHXW_ zH{qTMJOOI5l^IXXPMny+Umbu~QczUfL6X%l8<1MgD7GvilpcXMqx8*;d3FY%4nF1n z0YhxvbyW(DZ*m{R6yJ(rf2w|(yn!q|nEV=?Dm1ttin^l@n{MQ_aRIY?l=5upe274> z&Jk7#9`sGXXOKYYm>%&ZsR##~s96efw6|!Gx=r@r4T)PMqtX&J$Qpqmq!f&}<(!X2lMJ?P@qf>KCdM zj4yiJjB}n{m{Cy79{F*}@`wenc37+pY9fGHCz}=r1OZhP#!S=OY-yvLtf|uaQRh3w zIc8+bFiEWn=yVQhTZ8(`#sLq^>i?BZ^1u_wZ{I95P8XibW|JiGdZE?^-s4;K;KqD|X>{(c zjin1x&jalNFlzPUs%E3?UU~oJ?jDk!q{R2WC8=j7YV%dkor4Up$FjItxB%*}7XUYe zk1r>k!6YS5CVu(4@pxC1oqLO(B?|E-WF15opbh&JEP&70yAy6SxR@CK@TJKVvZzoN!4C*i}z=6PL_!tt5cUF#ql*xcAa z6QZb#qxcf10uTszpi}Y8&E5qKEK`pK6qSgtV?{i|8C6aDu@k%03qjSpady8 z0!p>^9B;_Y&2_2DuM?!d5YLU#Ej?(W?~Kp0 zO*A~ddeCTYr!qA&qfbrL1S^6cYwwB}x&f>T4$SJmcef7thgjVU3KEdSa+LgjJKVJe zaBh?9#pp)5_rHGqa)m4h_ReBebMS2dtYY%3Pj~w9({|7=MauFy&=Z%{KYP%5%7)98 zPzphmuZxy4yz3Aj&|!idz4gx(_#1vQ;%o;AvV58i%YFIEFW!HP>)^2R5O@dU8xWN=8JmQr}L@%W`pi8T{UK;@^|f$#2Q1za25p{+;cun-8xw zFWc2BvSf|X{B#D&liHD9dWY_Ap7sKbYffm!#_--xaLu(}HwNaPpLQP{r!d^eTCnc< zEqW@;XY^ZYLIrRRoS)2+Nnwdw2Ue&7PI0rM0uxer`2e@XL0isVH|W+qxc|T|)3nz) zX4vU8JV0Hy7tTV5h8{;Y^)g*7JsZ6jQFjZb_r(!jKhh0^mlD&YGM;Xiawwb+HQ_;G zNnDVMS<>$${1t&~_dK-?cez-wi;jra2OyNQo7}oBDHQnTR7S%g*({yT9i9fLkWN6O zcZ<^6N7;sEITXFQHRBJxCU@MXI8=&__*JFBdP^v6Y91KC1hL_&q=cHEeWu<3%7EZp zAJgh6+Yj>eltMM?dK^7C#YAt(*x#D5V@#A57b~q1{rs_nQELBV8FAX7;@wT%o75X= zI{adk#Mi*yYDo`uBK`cUw4rdub-+=asSH0C-?xJQ2(4Mt_RPQ`{9@Qn?5Y70d=OOmhAiz#(}Ly&qV37 zEG$7*lcQ{UVpl$`2_>MT&M{(0gHFNSUzwF4RYpfpEbd>VW0ndeoZ1`@zG+PJLv@(lskRbb-o^NC`GUYfOgd&iFkmB;En*s!%b)mF~G|RrF;ofBN)g+8$c5sat!2MGP=wrAj!_aLV>^WNEuRW(a#XCUYK`qo;%E7{7)DnuJWmW1chEb9-s$kr0= zbx}Lq7*1eWqB46r#@{8CF?2c0v#1nZZ~3;0v_^)+##>EK70PPjLJg-3rG>x-hvZemxeJX6fc@|RUr?A z#uY#dVVkH0@ShL(5~$mY0{#b!%7GhG7N{wgg&F7;v>a9)D1tlzkitSq#u$fHztCY5 z%2Y>1r0pXGdVvH|e@XOZ$ zh=N0%sBRgcI7eoPHG^>3nk~6^bPQ2FF1&@19^BjBw8vQ7t z?Z+EMNd)?zqJaGh`P2K}kjUU6K?V8`#R6H+E-iKU3`kDx9F2pfT!Q0sN=TsRBPmP( zCYAD2u~C^K_YLWQ8}rY3wNu7lel*iWIP}32wHnxU}rVh>@Kq^BdiTHIIYy^J~Cv=38))O$lUn+%*F!d5h-zk-7 zdg!5n&H=Rpa5ZZjt)r`wdpnG%#wl(xH|+WtX*0_-FBy>s&PFUY0;c`|ftAEvhW+QV zT2E3*#xp1Uor*d|;7L@U6~TT(TmVzN9DE=PtO{uTU<=9>Du`M1>2D_%=m#|4JT8~B zCxnzM)o%KIHH8X8>OK*Gk`S}Ov22q`rM!D?Flu);^=rsxDs@@H2VSWkE1)oPeVRkD zq>IX?oK;*s6oE(-GI97W7{FqoQ|ueo2|3>Qo3SL8ilH#LSP~C-6DT?Qp1$&oT;7%N zam`}ZL3*61-{(sIIFovlmn3(7gWq%Ql_PDu~eWvskuVax*XF~6?g>I(F zAW=MHD8D2#1nP-+mt~~@c6t+tCI)Gn{%kn2_&ert2+*Ih_c#)ojojMbmJ!+ywxjww zcJ8^eCyUf?eo*yu7+!s1e|nfESgN`|uN5f~O#IC^PVU+`h!BK@w^8@!v;S~YaT`=W z3mFus@SrzUwC%Y@wp{YGu?3mqYnyEJp)bL?fx&8_p6jj@o;|Q#5Rdmj+uCL?=ow_0 z4ZQ=b(19FFh~=iD{qMANC-^=#{!Fxq11n%yZ{_USR}9(RJ@233Q1^BQ<3jjHI4}M$ z$=NPqD|qoXe*L!Vod=h-ErXcne~Zj7ogZABi!~57BV`;0xVPA-rH!whENobtr8I1Y zsYX)b7M@+G^#t~M!Gbs){a@cqA~Wzg1T}nG_?y@IPubwcG#B6-CRlH(U$y7O{ye-$ z_fL8t4vz;!YF5D+A3v!B8+e-NysR2*wAuveBUG|8tJ?Ttxc=EGm)oiuy=@3@JEPez z3r?Fy_$!o-343<%m+%BYRYb$D3iuOElrK>d69Ako0m@3O&d8Ak3yfo1)n%L<(L>sz zkuk1P9Uy51Z>Rt?TP8rN%N+~lgNhNpKwsrA_4~B*I}<1^RKnu?^=_$g)M=)LJn@04 z@o+N4h)AxUT!92?wnh8K!n^3qUvUPkjfDe-su_M=$3iXrg4ti%Yo-IkV&GfnW7D{tDP#C@77FxJA-UUR?$zj{_(UM zfPM`E^&p_A%`d=g;Y_=t`#rKic zAqVTkZR=s72>AK*$L1FR7#lPbn0m1!*g{TBa{lJAmEaRYpT8)YO1fj$}`352DUwx0xrLZ@o+sJ0taBd4Z|U;v2X#iO4cvZv`LaFH5!UX}QAhty>F zt2_4X&mg-U(^B(*d}rJH$^+bU570MQ)BNmqR)Ow459#nGR%}K zzw4%Yfq>JiqK6XVSRfeyt{V7QfDdk&)KOqj^Uqct73s9v9bC}-{Oh?ScMC57TVw!g z+c*SVn+8OtOipoK1+)(zr&VQ0IP0PW$VncM@QOdx__Sq9 zZL#-2Ua_g@!*?qpWuOkoPI^Jr>4v&{?<=d>uu}UCXd0y^L7_50($6&22cg6&Gep7q z*?uK*;8_wvgeoUK?af9Mly9{wvPq#rzkFt#Jp8Ai+dvrk(J$W;>zzD&mm1fZ`0E>` z_P-z1eQ)f4WT6`0aW|ynEx{tMk?emrn%BQ`#G++;L%TNvEGFrPl`id${ckEZ1cP?# z1C`v)0h;1to#t2-?*BVP@O<8D;r)A}13XO$))Nn5sg$^X`*84{sbdl}vR3<_5kRtW zKmfT-oQ_oPsc;R2%?m{iPA6!t%A6` zyu6X)PqDeINn*%w&^(v}CCJMwcYXR`z%{J2L`|0&Za4Ho~^eEHx9HZpo@dJo} zaCRC!Cs+y)$Tgd)MyNkMIAiiDr+Ijd z3^ECy(kXjVhY0REltdxpSLPt#xcVQeF&?kLgHaGGod#_hDT!u_7&fDh4801$_&Wg1 zJp+@6O>}{Og8kr`+kHQ)hYH3@m=4Om-nE$AmX%bhk|SV1=NcX*^j z=~NVV{1hMS(RTsm-W*-Yq~wR+QAv``EIOYrrlvKgI8fJR5eiN8s&B6b0>N)+y2hpf z+le; Date: Thu, 13 Jun 2024 20:46:13 -0500 Subject: [PATCH 02/54] fix: image logo --- templates/compose/glances.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/compose/glances.yaml b/templates/compose/glances.yaml index 68894ee34..fdf27e5f4 100644 --- a/templates/compose/glances.yaml +++ b/templates/compose/glances.yaml @@ -1,7 +1,7 @@ # documentation: https://nicolargo.github.io/glances/ # slogan: An Eye on your system # tags: monitoring tool python cross platform -# logo: svgs/glances.svg +# logo: svgs/glances.png # port: 61208 services: From fe26b3d7590397e6648fd7947f308893c046f187 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 25 Jun 2024 14:20:21 +0200 Subject: [PATCH 03/54] chore: Update version to 4.0.0-beta.307 --- config/sentry.php | 2 +- config/version.php | 2 +- versions.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/sentry.php b/config/sentry.php index d8f111fa1..ec3f7ae60 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.306', + 'release' => '4.0.0-beta.307', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index a461f1a7a..ac5a388b8 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ Date: Tue, 25 Jun 2024 14:21:25 +0200 Subject: [PATCH 04/54] service: glances --- templates/service-templates.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/service-templates.json b/templates/service-templates.json index 4b860acaf..268e410f5 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -1 +1 @@ -{"activepieces":{"documentation":"https:\/\/www.activepieces.com\/docs\/getting-started\/introduction?utm_source=coolify.io","slogan":"Open source no-code business automation.","compose":"c2VydmljZXM6CiAgYWN0aXZlcGllY2VzOgogICAgaW1hZ2U6ICdnaGNyLmlvL2FjdGl2ZXBpZWNlcy9hY3RpdmVwaWVjZXM6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gQVBfQVBJX0tFWT0kU0VSVklDRV9QQVNTV09SRF82NF9BUElLRVkKICAgICAgLSBBUF9FTkNSWVBUSU9OX0tFWT0kU0VSVklDRV9QQVNTV09SRF9FTkNSWVBUSU9OS0VZCiAgICAgIC0gQVBfRU5HSU5FX0VYRUNVVEFCTEVfUEFUSD1kaXN0L3BhY2thZ2VzL2VuZ2luZS9tYWluLmpzCiAgICAgIC0gQVBfRU5WSVJPTk1FTlQ9cHJvZAogICAgICAtIEFQX0VYRUNVVElPTl9NT0RFPVVOU0FOREJPWEVECiAgICAgIC0gQVBfRlJPTlRFTkRfVVJMPSRTRVJWSUNFX0ZRRE5fQUNUSVZFUElFQ0VTCiAgICAgIC0gQVBfSldUX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9KV1QKICAgICAgLSBBUF9QT1NUR1JFU19EQVRBQkFTRT1hY3RpdmVwaWVjZXMKICAgICAgLSBBUF9QT1NUR1JFU19IT1NUPXBvc3RncmVzCiAgICAgIC0gQVBfUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSBBUF9QT1NUR1JFU19QT1JUPTU0MzIKICAgICAgLSBBUF9QT1NUR1JFU19VU0VSTkFNRT0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gQVBfUkVESVNfSE9TVD1yZWRpcwogICAgICAtIEFQX1JFRElTX1BPUlQ9NjM3OQogICAgICAtIEFQX1NBTkRCT1hfUlVOX1RJTUVfU0VDT05EUz02MDAKICAgICAgLSBBUF9URUxFTUVUUllfRU5BQkxFRD10cnVlCiAgICAgIC0gJ0FQX1RFTVBMQVRFU19TT1VSQ0VfVVJMPWh0dHBzOi8vY2xvdWQuYWN0aXZlcGllY2VzLmNvbS9hcGkvdjEvZmxvdy10ZW1wbGF0ZXMnCiAgICAgIC0gQVBfVFJJR0dFUl9ERUZBVUxUX1BPTExfSU5URVJWQUw9NQogICAgICAtIEFQX1dFQkhPT0tfVElNRU9VVF9TRUNPTkRTPTMwCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3JlczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICByZWRpczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2Vfc3RhcnRlZAogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX0RCPWFjdGl2ZXBpZWNlcwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICB2b2x1bWVzOgogICAgICAtICdwZy1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAncmVkaXNfZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSByZWRpcy1jbGkKICAgICAgICAtIHBpbmcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["workflow","automation","no code","open source"],"logo":"svgs\/activepieces.png","minversion":"0.0.0"},"appsmith":{"documentation":"https:\/\/appsmith.com?utm_source=coolify.io","slogan":"A low-code application platform for building internal tools.","compose":"c2VydmljZXM6CiAgYXBwc21pdGg6CiAgICBpbWFnZTogJ2luZGV4LmRvY2tlci5pby9hcHBzbWl0aC9hcHBzbWl0aC1jZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fQVBQU01JVEgKICAgICAgLSBBUFBTTUlUSF9NQUlMX0VOQUJMRUQ9ZmFsc2UKICAgICAgLSBBUFBTTUlUSF9ESVNBQkxFX1RFTEVNRVRSWT10cnVlCiAgICAgIC0gQVBQU01JVEhfRElTQUJMRV9JTlRFUkNPTT10cnVlCiAgICAgIC0gQVBQU01JVEhfU0VOVFJZX0RTTj0KICAgICAgLSBBUFBTTUlUSF9TTUFSVF9MT09LX0lEPQogICAgdm9sdW1lczoKICAgICAgLSAnc3RhY2tzLWRhdGE6L2FwcHNtaXRoLXN0YWNrcycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["lowcode","nocode","no","low","platform"],"logo":"svgs\/appsmith.svg","minversion":"0.0.0"},"appwrite":{"documentation":"https:\/\/appwrite.io?utm_source=coolify.io","slogan":"A backend-as-a-service platform that simplifies the web & mobile app development.","compose":"eC1sb2dnaW5nOgogIGxvZ2dpbmc6CiAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgb3B0aW9uczoKICAgICAgbWF4LWZpbGU6ICc1JwogICAgICBtYXgtc2l6ZTogMTBtCnNlcnZpY2VzOgogIGFwcHdyaXRlOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLXVwbG9hZHM6L3N0b3JhZ2UvdXBsb2FkczpydycKICAgICAgLSAnYXBwd3JpdGUtY2FjaGU6L3N0b3JhZ2UvY2FjaGU6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNvbmZpZzovc3RvcmFnZS9jb25maWc6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNlcnRpZmljYXRlczovc3RvcmFnZS9jZXJ0aWZpY2F0ZXM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWZ1bmN0aW9uczovc3RvcmFnZS9mdW5jdGlvbnM6cncnCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0FQUFdSSVRFPS8KICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9MT0NBTEUKICAgICAgLSBfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX1JPT1QKICAgICAgLSBfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX0VNQUlMUwogICAgICAtIF9BUFBfQ09OU09MRV9XSElURUxJU1RfSVBTCiAgICAgIC0gX0FQUF9DT05TT0xFX0hPU1ROQU1FUwogICAgICAtIF9BUFBfU1lTVEVNX0VNQUlMX05BTUUKICAgICAgLSBfQVBQX1NZU1RFTV9FTUFJTF9BRERSRVNTCiAgICAgIC0gX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfU1lTVEVNX1JFU1BPTlNFX0ZPUk1BVAogICAgICAtIF9BUFBfT1BUSU9OU19BQlVTRQogICAgICAtIF9BUFBfT1BUSU9OU19GT1JDRV9IVFRQUwogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX0RPTUFJTj0kU0VSVklDRV9GUUROX0FQUFdSSVRFCiAgICAgIC0gX0FQUF9ET01BSU5fVEFSR0VUPSRTRVJWSUNFX0ZRRE5fQVBQV1JJVEUKICAgICAgLSBfQVBQX0RPTUFJTl9GVU5DVElPTlM9JFNFUlZJQ0VfRlFETl9BUFBXUklURQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9TTVRQX0hPU1QKICAgICAgLSBfQVBQX1NNVFBfUE9SVAogICAgICAtIF9BUFBfU01UUF9TRUNVUkUKICAgICAgLSBfQVBQX1NNVFBfVVNFUk5BTUUKICAgICAgLSBfQVBQX1NNVFBfUEFTU1dPUkQKICAgICAgLSBfQVBQX1VTQUdFX1NUQVRTCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTUlUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1BSRVZJRVdfTElNSVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQU5USVZJUlVTCiAgICAgIC0gX0FQUF9TVE9SQUdFX0FOVElWSVJVU19IT1NUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0FOVElWSVJVU19QT1JUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RFVklDRQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9TM19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQlVDS0VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfU0laRV9MSU1JVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19CVUlMRF9USU1FT1VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfQ1BVUwogICAgICAtIF9BUFBfRlVOQ1RJT05TX01FTU9SWQogICAgICAtIF9BUFBfRlVOQ1RJT05TX1JVTlRJTUVTCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9TRUNSRVQKICAgICAgLSBfQVBQX0VYRUNVVE9SX0hPU1QKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9JTlRFUlZBTAogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfREVMQVkKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9FWEVDVVRJT04KICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9DQUNIRQogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FCVVNFCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQVVESVQKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9VU0FHRV9IT1VSTFkKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9TQ0hFRFVMRVMKICAgICAgLSBfQVBQX1NNU19QUk9WSURFUgogICAgICAtIF9BUFBfU01TX0ZST00KICAgICAgLSBfQVBQX0dSQVBIUUxfTUFYX0JBVENIX1NJWkUKICAgICAgLSBfQVBQX0dSQVBIUUxfTUFYX0NPTVBMRVhJVFkKICAgICAgLSBfQVBQX0dSQVBIUUxfTUFYX0RFUFRICiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX0FQUF9OQU1FCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX1BSSVZBVEVfS0VZCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX0FQUF9JRAogICAgICAtIF9BUFBfVkNTX0dJVEhVQl9XRUJIT09LX1NFQ1JFVAogICAgICAtIF9BUFBfVkNTX0dJVEhVQl9DTElFTlRfU0VDUkVUCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX0NMSUVOVF9JRAogICAgICAtIF9BUFBfTUlHUkFUSU9OU19GSVJFQkFTRV9DTElFTlRfSUQKICAgICAgLSBfQVBQX01JR1JBVElPTlNfRklSRUJBU0VfQ0xJRU5UX1NFQ1JFVAogICAgICAtIF9BUFBfQVNTSVNUQU5UX09QRU5BSV9BUElfS0VZCiAgYXBwd3JpdGUtcmVhbHRpbWU6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHJlYWx0aW1lCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0FQUFdSSVRFPS92MS9yZWFsdGltZQogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QVElPTlNfQUJVU0UKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX1VTQUdFX1NUQVRTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1hdWRpdHM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1hdWRpdHMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItYXVkaXRzCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICAgIC0gYXBwd3JpdGUtbWFyaWFkYgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItd2ViaG9va3M6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci13ZWJob29rcwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci13ZWJob29rcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItZGVsZXRlczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41JwogICAgZW50cnlwb2ludDogd29ya2VyLWRlbGV0ZXMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItZGVsZXRlcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLXVwbG9hZHM6L3N0b3JhZ2UvdXBsb2FkczpydycKICAgICAgLSAnYXBwd3JpdGUtY2FjaGU6L3N0b3JhZ2UvY2FjaGU6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWZ1bmN0aW9uczovc3RvcmFnZS9mdW5jdGlvbnM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWJ1aWxkczovc3RvcmFnZS9idWlsZHM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNlcnRpZmljYXRlczovc3RvcmFnZS9jZXJ0aWZpY2F0ZXM6cncnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RFVklDRQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9TM19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQlVDS0VUCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfRVhFQ1VUT1JfU0VDUkVUCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9IT1NUCiAgYXBwd3JpdGUtd29ya2VyLWRhdGFiYXNlczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41JwogICAgZW50cnlwb2ludDogd29ya2VyLWRhdGFiYXNlcwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1kYXRhYmFzZXMKICAgIGRlcGVuZHNfb246CiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgICAgLSBhcHB3cml0ZS1tYXJpYWRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1idWlsZHM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1idWlsZHMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItYnVpbGRzCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICAgIC0gYXBwd3JpdGUtbWFyaWFkYgogICAgdm9sdW1lczoKICAgICAgLSAnYXBwd3JpdGUtZnVuY3Rpb25zOi9zdG9yYWdlL2Z1bmN0aW9uczpydycKICAgICAgLSAnYXBwd3JpdGUtYnVpbGRzOi9zdG9yYWdlL2J1aWxkczpydycKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9TRUNSRVQKICAgICAgLSBfQVBQX0VYRUNVVE9SX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX05BTUUKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfUFJJVkFURV9LRVkKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX0lECiAgICAgIC0gX0FQUF9GVU5DVElPTlNfVElNRU9VVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX0JVSUxEX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19DUFVTCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfTUVNT1JZCiAgICAgIC0gX0FQUF9PUFRJT05TX0ZPUkNFX0hUVFBTCiAgICAgIC0gX0FQUF9ET01BSU4KICAgICAgLSBfQVBQX1NUT1JBR0VfREVWSUNFCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9TM19CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9CVUNLRVQKICBhcHB3cml0ZS13b3JrZXItY2VydGlmaWNhdGVzOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItY2VydGlmaWNhdGVzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtd29ya2VyLWNlcnRpZmljYXRlcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLWNvbmZpZzovc3RvcmFnZS9jb25maWc6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNlcnRpZmljYXRlczovc3RvcmFnZS9jZXJ0aWZpY2F0ZXM6cncnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfRE9NQUlOCiAgICAgIC0gX0FQUF9ET01BSU5fVEFSR0VUCiAgICAgIC0gX0FQUF9ET01BSU5fRlVOQ1RJT05TCiAgICAgIC0gX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1mdW5jdGlvbnM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1mdW5jdGlvbnMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItZnVuY3Rpb25zCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICAgIC0gYXBwd3JpdGUtbWFyaWFkYgogICAgICAtIG9wZW5ydW50aW1lcy1leGVjdXRvcgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfRlVOQ1RJT05TX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19CVUlMRF9USU1FT1VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfQ1BVUwogICAgICAtIF9BUFBfRlVOQ1RJT05TX01FTU9SWQogICAgICAtIF9BUFBfRVhFQ1VUT1JfU0VDUkVUCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9IT1NUCiAgICAgIC0gX0FQUF9VU0FHRV9TVEFUUwogICAgICAtIF9BUFBfRE9DS0VSX0hVQl9VU0VSTkFNRQogICAgICAtIF9BUFBfRE9DS0VSX0hVQl9QQVNTV09SRAogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICBhcHB3cml0ZS13b3JrZXItbWFpbHM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1tYWlscwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1tYWlscwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1NZU1RFTV9FTUFJTF9OQU1FCiAgICAgIC0gX0FQUF9TWVNURU1fRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfU01UUF9IT1NUCiAgICAgIC0gX0FQUF9TTVRQX1BPUlQKICAgICAgLSBfQVBQX1NNVFBfU0VDVVJFCiAgICAgIC0gX0FQUF9TTVRQX1VTRVJOQU1FCiAgICAgIC0gX0FQUF9TTVRQX1BBU1NXT1JECiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1tZXNzYWdpbmc6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1tZXNzYWdpbmcKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItbWVzc2FnaW5nCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfU01TX0ZST00KICAgICAgLSBfQVBQX1NNU19QUk9WSURFUgogIGFwcHdyaXRlLXdvcmtlci1taWdyYXRpb25zOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItbWlncmF0aW9ucwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1taWdyYXRpb25zCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9ET01BSU4KICAgICAgLSBfQVBQX0RPTUFJTl9UQVJHRVQKICAgICAgLSBfQVBQX1NZU1RFTV9TRUNVUklUWV9FTUFJTF9BRERSRVNTCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgICAgIC0gX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9JRAogICAgICAtIF9BUFBfTUlHUkFUSU9OU19GSVJFQkFTRV9DTElFTlRfU0VDUkVUCiAgYXBwd3JpdGUtbWFpbnRlbmFuY2U6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IG1haW50ZW5hbmNlCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtbWFpbnRlbmFuY2UKICAgIGRlcGVuZHNfb246CiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX0RPTUFJTgogICAgICAtIF9BUFBfRE9NQUlOX1RBUkdFVAogICAgICAtIF9BUFBfRE9NQUlOX0ZVTkNUSU9OUwogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfSU5URVJWQUwKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9FWEVDVVRJT04KICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9DQUNIRQogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FCVVNFCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQVVESVQKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9VU0FHRV9IT1VSTFkKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9TQ0hFRFVMRVMKICBhcHB3cml0ZS13b3JrZXItdXNhZ2U6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNS4xJwogICAgZW50cnlwb2ludDogd29ya2VyLXVzYWdlCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtd29ya2VyLXVzYWdlCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX1VTQUdFX1NUQVRTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfVVNBR0VfQUdHUkVHQVRJT05fSU5URVJWQUwKICBhcHB3cml0ZS13b3JrZXItdXNhZ2UtZHVtcDoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41LjEnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItdXNhZ2UtZHVtcAogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci11c2FnZS1kdW1wCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICAgIC0gYXBwd3JpdGUtbWFyaWFkYgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfVVNBR0VfU1RBVFMKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgICAgIC0gX0FQUF9VU0FHRV9BR0dSRUdBVElPTl9JTlRFUlZBTAogIGFwcHdyaXRlLXNjaGVkdWxlci1mdW5jdGlvbnM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHNjaGVkdWxlLWZ1bmN0aW9ucwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXNjaGVkdWxlci1mdW5jdGlvbnMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogIGFwcHdyaXRlLXNjaGVkdWxlci1tZXNzYWdlczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41JwogICAgZW50cnlwb2ludDogc2NoZWR1bGUtbWVzc2FnZXMKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS1zY2hlZHVsZXItbWVzc2FnZXMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogIGFwcHdyaXRlLWFzc2lzdGFudDoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXNzaXN0YW50OjAuNC4wJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLWFzc2lzdGFudAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9BU1NJU1RBTlRfT1BFTkFJX0FQSV9LRVkKICBvcGVucnVudGltZXMtZXhlY3V0b3I6CiAgICBjb250YWluZXJfbmFtZTogb3BlbnJ1bnRpbWVzLWV4ZWN1dG9yCiAgICBob3N0bmFtZTogYXBwd3JpdGUtZXhlY3V0b3IKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHN0b3Bfc2lnbmFsOiBTSUdJTlQKICAgIGltYWdlOiAnb3BlbnJ1bnRpbWVzL2V4ZWN1dG9yOjAuNC45JwogICAgdm9sdW1lczoKICAgICAgLSAnL3Zhci9ydW4vZG9ja2VyLnNvY2s6L3Zhci9ydW4vZG9ja2VyLnNvY2snCiAgICAgIC0gJ2FwcHdyaXRlLWJ1aWxkczovc3RvcmFnZS9idWlsZHM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWZ1bmN0aW9uczovc3RvcmFnZS9mdW5jdGlvbnM6cncnCiAgICAgIC0gJy90bXA6L3RtcDpydycKICAgIGVudmlyb25tZW50OgogICAgICAtIE9QUl9FWEVDVVRPUl9JTkFDVElWRV9UUkVTSE9MRD0kX0FQUF9GVU5DVElPTlNfSU5BQ1RJVkVfVEhSRVNIT0xECiAgICAgIC0gT1BSX0VYRUNVVE9SX01BSU5URU5BTkNFX0lOVEVSVkFMPSRfQVBQX0ZVTkNUSU9OU19NQUlOVEVOQU5DRV9JTlRFUlZBTAogICAgICAtIE9QUl9FWEVDVVRPUl9ORVRXT1JLPSRfQVBQX0ZVTkNUSU9OU19SVU5USU1FU19ORVRXT1JLCiAgICAgIC0gT1BSX0VYRUNVVE9SX0RPQ0tFUl9IVUJfVVNFUk5BTUU9JF9BUFBfRE9DS0VSX0hVQl9VU0VSTkFNRQogICAgICAtIE9QUl9FWEVDVVRPUl9ET0NLRVJfSFVCX1BBU1NXT1JEPSRfQVBQX0RPQ0tFUl9IVUJfUEFTU1dPUkQKICAgICAgLSBPUFJfRVhFQ1VUT1JfRU5WPSRfQVBQX0VOVgogICAgICAtIE9QUl9FWEVDVVRPUl9SVU5USU1FUz0kX0FQUF9GVU5DVElPTlNfUlVOVElNRVMKICAgICAgLSBPUFJfRVhFQ1VUT1JfU0VDUkVUPSRfQVBQX0VYRUNVVE9SX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9MT0dHSU5HX1BST1ZJREVSPSRfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBPUFJfRVhFQ1VUT1JfTE9HR0lOR19DT05GSUc9JF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9ERVZJQ0U9JF9BUFBfU1RPUkFHRV9ERVZJQ0UKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9TM19BQ0NFU1NfS0VZPSRfQVBQX1NUT1JBR0VfUzNfQUNDRVNTX0tFWQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1MzX1NFQ1JFVD0kX0FQUF9TVE9SQUdFX1MzX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1MzX1JFR0lPTj0kX0FQUF9TVE9SQUdFX1MzX1JFR0lPTgogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1MzX0JVQ0tFVD0kX0FQUF9TVE9SQUdFX1MzX0JVQ0tFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0RPX1NQQUNFU19BQ0NFU1NfS0VZPSRfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0FDQ0VTU19LRVkKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9ET19TUEFDRVNfU0VDUkVUPSRfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT049JF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfUkVHSU9OCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVD0kX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19CVUNLRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9CQUNLQkxBWkVfQUNDRVNTX0tFWT0kX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfQkFDS0JMQVpFX1NFQ1JFVD0kX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9TRUNSRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9CQUNLQkxBWkVfUkVHSU9OPSRfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1JFR0lPTgogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0JBQ0tCTEFaRV9CVUNLRVQ9JF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQlVDS0VUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfTElOT0RFX0FDQ0VTU19LRVk9JF9BUFBfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0xJTk9ERV9TRUNSRVQ9JF9BUFBfU1RPUkFHRV9MSU5PREVfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfTElOT0RFX1JFR0lPTj0kX0FQUF9TVE9SQUdFX0xJTk9ERV9SRUdJT04KICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9MSU5PREVfQlVDS0VUPSRfQVBQX1NUT1JBR0VfTElOT0RFX0JVQ0tFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1dBU0FCSV9BQ0NFU1NfS0VZPSRfQVBQX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVkKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9XQVNBQklfU0VDUkVUPSRfQVBQX1NUT1JBR0VfV0FTQUJJX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1dBU0FCSV9SRUdJT049JF9BUFBfU1RPUkFHRV9XQVNBQklfUkVHSU9OCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfV0FTQUJJX0JVQ0tFVD0kX0FQUF9TVE9SQUdFX1dBU0FCSV9CVUNLRVQKICBhcHB3cml0ZS1tYXJpYWRiOgogICAgaW1hZ2U6ICdtYXJpYWRiOjEwLjExJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLW1hcmlhZGIKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLW1hcmlhZGI6L3Zhci9saWIvbXlzcWw6cncnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke19BUFBfREJfUk9PVF9QQVNTfScKICAgICAgLSAnTVlTUUxfREFUQUJBU0U9JHtfQVBQX0RCX1NDSEVNQX0nCiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtfQVBQX0RCX1VTRVJ9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke19BUFBfREJfUEFTU30nCiAgICBjb21tYW5kOiAnbXlzcWxkIC0taW5ub2RiLWZsdXNoLW1ldGhvZD1mc3luYycKICBhcHB3cml0ZS1yZWRpczoKICAgIGltYWdlOiAncmVkaXM6Ny4yLjQtYWxwaW5lJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXJlZGlzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb21tYW5kOiAicmVkaXMtc2VydmVyIC0tbWF4bWVtb3J5ICAgICAgICAgICAgNTEybWIgLS1tYXhtZW1vcnktcG9saWN5ICAgICBhbGxrZXlzLWxydSAtLW1heG1lbW9yeS1zYW1wbGVzICAgIDVcbiIKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLXJlZGlzOi9kYXRhOnJ3Jwp2b2x1bWVzOgogIGFwcHdyaXRlLW1hcmlhZGI6IG51bGwKICBhcHB3cml0ZS1yZWRpczogbnVsbAogIGFwcHdyaXRlLWNhY2hlOiBudWxsCiAgYXBwd3JpdGUtdXBsb2FkczogbnVsbAogIGFwcHdyaXRlLWNlcnRpZmljYXRlczogbnVsbAogIGFwcHdyaXRlLWZ1bmN0aW9uczogbnVsbAogIGFwcHdyaXRlLWJ1aWxkczogbnVsbAogIGFwcHdyaXRlLWNvbmZpZzogbnVsbAo=","tags":["backend-as-a-service","platform"],"logo":"svgs\/appwrite.svg","minversion":"0.0.0","envs":"X0FQUF9FTlY9cHJvZHVjdGlvbgpfQVBQX0xPQ0FMRT1lbgpfQVBQX09QVElPTlNfQUJVU0U9ZW5hYmxlZApfQVBQX09QVElPTlNfRk9SQ0VfSFRUUFM9ZGlzYWJsZWQKX0FQUF9PUEVOU1NMX0tFWV9WMT0KX0FQUF9ET01BSU49Cl9BUFBfRE9NQUlOX1RBUkdFVD0KX0FQUF9ET01BSU5fRlVOQ1RJT05TPQpfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX1JPT1Q9ZW5hYmxlZApfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX0VNQUlMUz0KX0FQUF9DT05TT0xFX1dISVRFTElTVF9JUFM9Cl9BUFBfQ09OU09MRV9IT1NUTkFNRVM9bG9jYWxob3N0LGFwcHdyaXRlLmlvLCouYXBwd3JpdGUuaW8KX0FQUF9TWVNURU1fRU1BSUxfTkFNRT1BcHB3cml0ZQpfQVBQX1NZU1RFTV9FTUFJTF9BRERSRVNTPXRlYW1AYXBwd3JpdGUuaW8KX0FQUF9TWVNURU1fUkVTUE9OU0VfRk9STUFUPQpfQVBQX1NZU1RFTV9TRUNVUklUWV9FTUFJTF9BRERSRVNTPWNlcnRzQGFwcHdyaXRlLmlvCl9BUFBfVVNBR0VfU1RBVFM9ZW5hYmxlZApfQVBQX0xPR0dJTkdfUFJPVklERVI9Cl9BUFBfTE9HR0lOR19DT05GSUc9Cl9BUFBfVVNBR0VfQUdHUkVHQVRJT05fSU5URVJWQUw9MzAKX0FQUF9VU0FHRV9USU1FU0VSSUVTX0lOVEVSVkFMPTMwCl9BUFBfVVNBR0VfREFUQUJBU0VfSU5URVJWQUw9OTAwCl9BUFBfV09SS0VSX1BFUl9DT1JFPTYKX0FQUF9SRURJU19IT1NUPWFwcHdyaXRlLXJlZGlzCl9BUFBfUkVESVNfUE9SVD02Mzc5Cl9BUFBfUkVESVNfVVNFUj0KX0FQUF9SRURJU19QQVNTPQpfQVBQX0RCX0hPU1Q9YXBwd3JpdGUtbWFyaWFkYgpfQVBQX0RCX1BPUlQ9MzMwNgpfQVBQX0RCX1NDSEVNQT1hcHB3cml0ZQpfQVBQX0RCX1VTRVI9JFNFUlZJQ0VfVVNFUl9NWVNRTApfQVBQX0RCX1BBU1M9JFNFUlZJQ0VfUEFTU1dPUkRfTVlTUUwKX0FQUF9EQl9ST09UX1BBU1M9JFNFUlZJQ0VfUEFTU1dPUkRfUk9PVE1ZU1FMCl9BUFBfU01UUF9IT1NUPQpfQVBQX1NNVFBfUE9SVD0KX0FQUF9TTVRQX1NFQ1VSRT0KX0FQUF9TTVRQX1VTRVJOQU1FPQpfQVBQX1NNVFBfUEFTU1dPUkQ9Cl9BUFBfU01TX1BST1ZJREVSPQpfQVBQX1NNU19GUk9NPQpfQVBQX1NUT1JBR0VfTElNSVQ9MzAwMDAwMDAKX0FQUF9TVE9SQUdFX1BSRVZJRVdfTElNSVQ9MjAwMDAwMDAKX0FQUF9TVE9SQUdFX0FOVElWSVJVUz1kaXNhYmxlZApfQVBQX1NUT1JBR0VfQU5USVZJUlVTX0hPU1Q9YXBwd3JpdGUtY2xhbWF2Cl9BUFBfU1RPUkFHRV9BTlRJVklSVVNfUE9SVD0zMzEwCl9BUFBfU1RPUkFHRV9ERVZJQ0U9bG9jYWwKX0FQUF9TVE9SQUdFX1MzX0FDQ0VTU19LRVk9Cl9BUFBfU1RPUkFHRV9TM19TRUNSRVQ9Cl9BUFBfU1RPUkFHRV9TM19SRUdJT049dXMtZWFzdC0xCl9BUFBfU1RPUkFHRV9TM19CVUNLRVQ9Cl9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQUNDRVNTX0tFWT0KX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19TRUNSRVQ9Cl9BUFBfU1RPUkFHRV9ET19TUEFDRVNfUkVHSU9OPXVzLWVhc3QtMQpfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVD0KX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZPQpfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1NFQ1JFVD0KX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9SRUdJT049dXMtd2VzdC0wMDQKX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9CVUNLRVQ9Cl9BUFBfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWT0KX0FQUF9TVE9SQUdFX0xJTk9ERV9TRUNSRVQ9Cl9BUFBfU1RPUkFHRV9MSU5PREVfUkVHSU9OPWV1LWNlbnRyYWwtMQpfQVBQX1NUT1JBR0VfTElOT0RFX0JVQ0tFVD0KX0FQUF9TVE9SQUdFX1dBU0FCSV9BQ0NFU1NfS0VZPQpfQVBQX1NUT1JBR0VfV0FTQUJJX1NFQ1JFVD0KX0FQUF9TVE9SQUdFX1dBU0FCSV9SRUdJT049ZXUtY2VudHJhbC0xCl9BUFBfU1RPUkFHRV9XQVNBQklfQlVDS0VUPQpfQVBQX0ZVTkNUSU9OU19TSVpFX0xJTUlUPTMwMDAwMDAwCl9BUFBfRlVOQ1RJT05TX1RJTUVPVVQ9OTAwCl9BUFBfRlVOQ1RJT05TX0JVSUxEX1RJTUVPVVQ9OTAwCl9BUFBfRlVOQ1RJT05TX0NPTlRBSU5FUlM9MTAKX0FQUF9GVU5DVElPTlNfQ1BVUz0wCl9BUFBfRlVOQ1RJT05TX01FTU9SWT0wCl9BUFBfRlVOQ1RJT05TX01FTU9SWV9TV0FQPTAKX0FQUF9GVU5DVElPTlNfUlVOVElNRVM9bm9kZS0yMC4wLHBocC04LjIscHl0aG9uLTMuMTEscnVieS0zLjIKX0FQUF9FWEVDVVRPUl9TRUNSRVQ9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfQVBQV1JJVEUKX0FQUF9FWEVDVVRPUl9IT1NUPWh0dHA6Ly9hcHB3cml0ZS1leGVjdXRvci92MQpfQVBQX0VYRUNVVE9SX1JVTlRJTUVfTkVUV09SSz1hcHB3cml0ZV9ydW50aW1lcwpfQVBQX0ZVTkNUSU9OU19JTkFDVElWRV9USFJFU0hPTEQ9NjAKRE9DS0VSSFVCX1BVTExfVVNFUk5BTUU9CkRPQ0tFUkhVQl9QVUxMX1BBU1NXT1JEPQpET0NLRVJIVUJfUFVMTF9FTUFJTD0KT1BFTl9SVU5USU1FU19ORVRXT1JLPWFwcHdyaXRlX3J1bnRpbWVzCl9BUFBfRlVOQ1RJT05TX1JVTlRJTUVTX05FVFdPUks9cnVudGltZXMKX0FQUF9ET0NLRVJfSFVCX1VTRVJOQU1FPQpfQVBQX0RPQ0tFUl9IVUJfUEFTU1dPUkQ9Cl9BUFBfRlVOQ1RJT05TX01BSU5URU5BTkNFX0lOVEVSVkFMPTM2MDAKX0FQUF9WQ1NfR0lUSFVCX0FQUF9OQU1FPQpfQVBQX1ZDU19HSVRIVUJfUFJJVkFURV9LRVk9Cl9BUFBfVkNTX0dJVEhVQl9BUFBfSUQ9Cl9BUFBfVkNTX0dJVEhVQl9DTElFTlRfSUQ9Cl9BUFBfVkNTX0dJVEhVQl9DTElFTlRfU0VDUkVUPQpfQVBQX1ZDU19HSVRIVUJfV0VCSE9PS19TRUNSRVQ9Cl9BUFBfTUFJTlRFTkFOQ0VfREVMQVk9Cl9BUFBfTUFJTlRFTkFOQ0VfSU5URVJWQUw9ODY0MDAKX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQ0FDSEU9MjU5MjAwMApfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9FWEVDVVRJT049MTIwOTYwMApfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9BVURJVD0xMjA5NjAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FCVVNFPTg2NDAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1VTQUdFX0hPVVJMWT04NjQwMDAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1NDSEVEVUxFUz04NjQwMApfQVBQX0dSQVBIUUxfTUFYX0JBVENIX1NJWkU9MTAKX0FQUF9HUkFQSFFMX01BWF9DT01QTEVYSVRZPTI1MApfQVBQX0dSQVBIUUxfTUFYX0RFUFRIPTMKX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9JRD0KX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9TRUNSRVQ9Cl9BUFBfQVNTSVNUQU5UX09QRU5BSV9BUElfS0VZPQo="},"authentik":{"documentation":"https:\/\/docs.goauthentik.io\/docs\/installation\/docker-compose?utm_source=coolify.io","slogan":"An open-source Identity Provider, focused on flexibility and versatility.","compose":"c2VydmljZXM6CiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAnZG9ja2VyLmlvL2xpYnJhcnkvcG9zdGdyZXM6MTItYWxwaW5lJwogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtZCAkJHtQT1NUR1JFU19EQn0gLVUgJCR7UE9TVEdSRVNfVVNFUn0nCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2F1dGhlbnRpay1kYjovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtIFBPU1RHUkVTX0RCPWF1dGhlbnRpawogIHJlZGlzOgogICAgaW1hZ2U6ICdkb2NrZXIuaW8vbGlicmFyeS9yZWRpczphbHBpbmUnCiAgICBjb21tYW5kOiAnLS1zYXZlIDYwIDEgLS1sb2dsZXZlbCB3YXJuaW5nJwogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncmVkaXMtY2xpIHBpbmcgfCBncmVwIFBPTkcnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3JlZGlzOi9kYXRhJwogIGF1dGhlbnRpay1zZXJ2ZXI6CiAgICBpbWFnZTogJ2doY3IuaW8vZ29hdXRoZW50aWsvc2VydmVyOiR7QVVUSEVOVElLX1RBRzotMjAyNC4yLjJ9JwogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGNvbW1hbmQ6IHNlcnZlcgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0FVVEhFTlRJS1NFUlZFUl85MDAwCiAgICAgIC0gQVVUSEVOVElLX1JFRElTX19IT1NUPXJlZGlzCiAgICAgIC0gQVVUSEVOVElLX1BPU1RHUkVTUUxfX0hPU1Q9cG9zdGdyZXNxbAogICAgICAtICdBVVRIRU5USUtfUE9TVEdSRVNRTF9fVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMfScKICAgICAgLSBBVVRIRU5USUtfUE9TVEdSRVNRTF9fTkFNRT1hdXRoZW50aWsKICAgICAgLSAnQVVUSEVOVElLX1BPU1RHUkVTUUxfX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMfScKICAgICAgLSAnQVVUSEVOVElLX1NFQ1JFVF9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEXzY0X0FVVEhFTlRJS1NFUlZFUn0nCiAgICAgIC0gJ0FVVEhFTlRJS19FUlJPUl9SRVBPUlRJTkdfX0VOQUJMRUQ9JHtBVVRIRU5USUtfRVJST1JfUkVQT1JUSU5HX19FTkFCTEVEOi10cnVlfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19IT1NUPSR7QVVUSEVOVElLX0VNQUlMX19IT1NUfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19QT1JUPSR7QVVUSEVOVElLX0VNQUlMX19QT1JUfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19VU0VSTkFNRT0ke0FVVEhFTlRJS19FTUFJTF9fVVNFUk5BTUV9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1BBU1NXT1JEPSR7QVVUSEVOVElLX0VNQUlMX19QQVNTV09SRH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fVVNFX1RMUz0ke0FVVEhFTlRJS19FTUFJTF9fVVNFX1RMU30nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fVVNFX1NTTD0ke0FVVEhFTlRJS19FTUFJTF9fVVNFX1NTTH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fVElNRU9VVD0ke0FVVEhFTlRJS19FTUFJTF9fVElNRU9VVH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fRlJPTT0ke0FVVEhFTlRJS19FTUFJTF9fRlJPTX0nCiAgICB2b2x1bWVzOgogICAgICAtICcuL21lZGlhOi9tZWRpYScKICAgICAgLSAnLi9jdXN0b20tdGVtcGxhdGVzOi90ZW1wbGF0ZXMnCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3Jlc3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHJlZGlzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgYXV0aGVudGlrLXdvcmtlcjoKICAgIGltYWdlOiAnZ2hjci5pby9nb2F1dGhlbnRpay9zZXJ2ZXI6JHtBVVRIRU5USUtfVEFHOi0yMDI0LjIuMn0nCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgY29tbWFuZDogd29ya2VyCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBBVVRIRU5USUtfUkVESVNfX0hPU1Q9cmVkaXMKICAgICAgLSBBVVRIRU5USUtfUE9TVEdSRVNRTF9fSE9TVD1wb3N0Z3Jlc3FsCiAgICAgIC0gJ0FVVEhFTlRJS19QT1NUR1JFU1FMX19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtIEFVVEhFTlRJS19QT1NUR1JFU1FMX19OQU1FPWF1dGhlbnRpawogICAgICAtICdBVVRIRU5USUtfUE9TVEdSRVNRTF9fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdBVVRIRU5USUtfU0VDUkVUX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfQVVUSEVOVElLU0VSVkVSfScKICAgICAgLSAnQVVUSEVOVElLX0VSUk9SX1JFUE9SVElOR19fRU5BQkxFRD0ke0FVVEhFTlRJS19FUlJPUl9SRVBPUlRJTkdfX0VOQUJMRUR9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX0hPU1Q9JHtBVVRIRU5USUtfRU1BSUxfX0hPU1R9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1BPUlQ9JHtBVVRIRU5USUtfRU1BSUxfX1BPUlR9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1VTRVJOQU1FPSR7QVVUSEVOVElLX0VNQUlMX19VU0VSTkFNRX0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fUEFTU1dPUkQ9JHtBVVRIRU5USUtfRU1BSUxfX1BBU1NXT1JEfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19VU0VfVExTPSR7QVVUSEVOVElLX0VNQUlMX19VU0VfVExTfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19VU0VfU1NMPSR7QVVUSEVOVElLX0VNQUlMX19VU0VfU1NMfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19USU1FT1VUPSR7QVVUSEVOVElLX0VNQUlMX19USU1FT1VUfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19GUk9NPSR7QVVUSEVOVElLX0VNQUlMX19GUk9NfScKICAgIHVzZXI6IHJvb3QKICAgIHZvbHVtZXM6CiAgICAgIC0gJy92YXIvcnVuL2RvY2tlci5zb2NrOi92YXIvcnVuL2RvY2tlci5zb2NrJwogICAgICAtICcuL21lZGlhOi9tZWRpYScKICAgICAgLSAnLi9jZXJ0czovY2VydHMnCiAgICAgIC0gJy4vY3VzdG9tLXRlbXBsYXRlczovdGVtcGxhdGVzJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICByZWRpczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQo=","tags":["identity","login","user","oauth","openid","oidc","authentication","saml","auth0","okta"],"logo":"svgs\/authentik.png","minversion":"0.0.0","port":"9000"},"babybuddy":{"documentation":"https:\/\/docs.baby-buddy.net?utm_source=coolify.io","slogan":"It helps parents track their baby's daily activities, growth, and health with ease.","compose":"c2VydmljZXM6CiAgYmFieWJ1ZGR5OgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL2JhYnlidWRkeTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fQkFCWUJVRERZCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgICAtIENTUkZfVFJVU1RFRF9PUklHSU5TPSRTRVJWSUNFX0ZRRE5fQkFCWUJVRERZCiAgICB2b2x1bWVzOgogICAgICAtICdiYWJ5YnVkZHktY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAwMCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=","tags":["baby","parents","health","growth","activities"],"logo":"svgs\/babybuddy.png","minversion":"0.0.0"},"budge":{"documentation":"https:\/\/github.com\/linuxserver\/budge?utm_source=coolify.io","slogan":"A budgeting personal finance app.","compose":"c2VydmljZXM6CiAgYnVkZ2U6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvYnVkZ2U6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0JVREdFCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnYnVkZ2UtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["personal finance","budgeting","expense tracking"],"logo":"svgs\/unknown.svg","minversion":"0.0.0"},"changedetection":{"documentation":"https:\/\/github.com\/dgtlmoon\/changedetection.io\/?utm_source=coolify.io","slogan":"Website change detection monitor and notifications.","compose":"c2VydmljZXM6CiAgY2hhbmdlZGV0ZWN0aW9uOgogICAgaW1hZ2U6IGdoY3IuaW8vZGd0bG1vb24vY2hhbmdlZGV0ZWN0aW9uLmlvCiAgICB2b2x1bWVzOgogICAgICAtICdjaGFuZ2VkZXRlY3Rpb24tZGF0YTovZGF0YXN0b3JlJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NIQU5HRURFVEVDVElPTl81MDAwCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gQkFTRV9VUkw9JFNFUlZJQ0VfRlFETl9DSEFOR0VERVRFQ1RJT04KICAgICAgLSAnUExBWVdSSUdIVF9EUklWRVJfVVJMPXdzOi8vcGxheXdyaWdodC1jaHJvbWU6MzAwMC8\/c3RlYWx0aD0xJi0tZGlzYWJsZS13ZWItc2VjdXJpdHk9dHJ1ZScKICAgICAgLSBISURFX1JFRkVSRVI9dHJ1ZQogICAgZGVwZW5kc19vbjoKICAgICAgcGxheXdyaWdodC1jaHJvbWU6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBlY2hvCiAgICAgICAgLSBvawogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcGxheXdyaWdodC1jaHJvbWU6CiAgICBpbWFnZTogJ2RndGxtb29uL3NvY2twdXBwZXRicm93c2VyOmxhdGVzdCcKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTQ1JFRU5fV0lEVEg9MTkyMAogICAgICAtIFNDUkVFTl9IRUlHSFQ9MTAyNAogICAgICAtIFNDUkVFTl9ERVBUSD0xNgogICAgICAtIE1BWF9DT05DVVJSRU5UX0NIUk9NRV9QUk9DRVNTRVM9MTAKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBlY2hvCiAgICAgICAgLSBvawogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["web","alert","monitor"],"logo":"svgs\/changedetection.png","minversion":"0.0.0","port":"5000"},"chatwoot":{"documentation":"https:\/\/www.chatwoot.com\/docs\/self-hosted\/?utm_source=coolify.io","slogan":"Delightful customer relationships at scale.","compose":"c2VydmljZXM6CiAgcmFpbHM6CiAgICBpbWFnZTogJ2NoYXR3b290L2NoYXR3b290OmxhdGVzdCcKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcG9zdGdyZXMKICAgICAgLSByZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NIQVRXT09UXzMwMDAKICAgICAgLSBTRUNSRVRfS0VZX0JBU0U9JFNFUlZJQ0VfUEFTU1dPUkRfQ0hBVFdPT1QKICAgICAgLSAnRlJPTlRFTkRfVVJMPSR7U0VSVklDRV9GUUROX0NIQVRXT09UfScKICAgICAgLSAnREVGQVVMVF9MT0NBTEU9JHtDSEFUV09PVF9ERUZBVUxUX0xPQ0FMRX0nCiAgICAgIC0gRk9SQ0VfU1NMPWZhbHNlCiAgICAgIC0gRU5BQkxFX0FDQ09VTlRfU0lHTlVQPWZhbHNlCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovL2RlZmF1bHRAcmVkaXM6NjM3OScKICAgICAgLSBSRURJU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9SRURJUwogICAgICAtIFJFRElTX09QRU5TU0xfVkVSSUZZX01PREU9bm9uZQogICAgICAtIFBPU1RHUkVTX0RBVEFCQVNFPWNoYXR3b290CiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1VTRVJOQU1FPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVNfVVNFUgogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gUkFJTFNfTUFYX1RIUkVBRFM9NQogICAgICAtIE5PREVfRU5WPXByb2R1Y3Rpb24KICAgICAgLSBSQUlMU19FTlY9cHJvZHVjdGlvbgogICAgICAtIElOU1RBTExBVElPTl9FTlY9ZG9ja2VyCiAgICAgIC0gJ01BSUxFUl9TRU5ERVJfRU1BSUw9JHtDSEFUV09PVF9NQUlMRVJfU0VOREVSX0VNQUlMfScKICAgICAgLSAnU01UUF9BRERSRVNTPSR7Q0hBVFdPT1RfU01UUF9BRERSRVNTfScKICAgICAgLSAnU01UUF9BVVRIRU5USUNBVElPTj0ke0NIQVRXT09UX1NNVFBfQVVUSEVOVElDQVRJT059JwogICAgICAtICdTTVRQX0RPTUFJTj0ke0NIQVRXT09UX1NNVFBfRE9NQUlOfScKICAgICAgLSAnU01UUF9FTkFCTEVfU1RBUlRUTFNfQVVUTz0ke0NIQVRXT09UX1NNVFBfRU5BQkxFX1NUQVJUVExTX0FVVE99JwogICAgICAtICdTTVRQX1BPUlQ9JHtDSEFUV09PVF9TTVRQX1BPUlR9JwogICAgICAtICdTTVRQX1VTRVJOQU1FPSR7Q0hBVFdPT1RfU01UUF9VU0VSTkFNRX0nCiAgICAgIC0gJ1NNVFBfUEFTU1dPUkQ9JHtDSEFUV09PVF9TTVRQX1BBU1NXT1JEfScKICAgICAgLSBBQ1RJVkVfU1RPUkFHRV9TRVJWSUNFPWxvY2FsCiAgICBlbnRyeXBvaW50OiBkb2NrZXIvZW50cnlwb2ludHMvcmFpbHMuc2gKICAgIGNvbW1hbmQ6ICdzaCAtYyAiYnVuZGxlIGV4ZWMgcmFpbHMgZGI6Y2hhdHdvb3RfcHJlcGFyZSAmJiBidW5kbGUgZXhlYyByYWlscyBzIC1wIDMwMDAgLWIgMC4wLjAuMCInCiAgICB2b2x1bWVzOgogICAgICAtICdyYWlscy1kYXRhOi9hcHAvc3RvcmFnZScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnLXEnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgc2lkZWtpcToKICAgIGltYWdlOiAnY2hhdHdvb3QvY2hhdHdvb3Q6bGF0ZXN0JwogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3JlcwogICAgICAtIHJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRUNSRVRfS0VZX0JBU0U9JFNFUlZJQ0VfUEFTU1dPUkRfQ0hBVFdPT1QKICAgICAgLSAnRlJPTlRFTkRfVVJMPSR7U0VSVklDRV9GUUROX0NIQVRXT09UfScKICAgICAgLSAnREVGQVVMVF9MT0NBTEU9JHtDSEFUV09PVF9ERUZBVUxUX0xPQ0FMRX0nCiAgICAgIC0gRk9SQ0VfU1NMPWZhbHNlCiAgICAgIC0gRU5BQkxFX0FDQ09VTlRfU0lHTlVQPWZhbHNlCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovL2RlZmF1bHRAcmVkaXM6NjM3OScKICAgICAgLSBSRURJU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9SRURJUwogICAgICAtIFJFRElTX09QRU5TU0xfVkVSSUZZX01PREU9bm9uZQogICAgICAtIFBPU1RHUkVTX0RBVEFCQVNFPWNoYXR3b290CiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1VTRVJOQU1FPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVNfVVNFUgogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gUkFJTFNfTUFYX1RIUkVBRFM9NQogICAgICAtIE5PREVfRU5WPXByb2R1Y3Rpb24KICAgICAgLSBSQUlMU19FTlY9cHJvZHVjdGlvbgogICAgICAtIElOU1RBTExBVElPTl9FTlY9ZG9ja2VyCiAgICAgIC0gJ01BSUxFUl9TRU5ERVJfRU1BSUw9JHtDSEFUV09PVF9NQUlMRVJfU0VOREVSX0VNQUlMfScKICAgICAgLSAnU01UUF9BRERSRVNTPSR7Q0hBVFdPT1RfU01UUF9BRERSRVNTfScKICAgICAgLSAnU01UUF9BVVRIRU5USUNBVElPTj0ke0NIQVRXT09UX1NNVFBfQVVUSEVOVElDQVRJT059JwogICAgICAtICdTTVRQX0RPTUFJTj0ke0NIQVRXT09UX1NNVFBfRE9NQUlOfScKICAgICAgLSAnU01UUF9FTkFCTEVfU1RBUlRUTFNfQVVUTz0ke0NIQVRXT09UX1NNVFBfRU5BQkxFX1NUQVJUVExTX0FVVE99JwogICAgICAtICdTTVRQX1BPUlQ9JHtDSEFUV09PVF9TTVRQX1BPUlR9JwogICAgICAtICdTTVRQX1VTRVJOQU1FPSR7Q0hBVFdPT1RfU01UUF9VU0VSTkFNRX0nCiAgICAgIC0gJ1NNVFBfUEFTU1dPUkQ9JHtDSEFUV09PVF9TTVRQX1BBU1NXT1JEfScKICAgICAgLSBBQ1RJVkVfU1RPUkFHRV9TRVJWSUNFPWxvY2FsCiAgICBjb21tYW5kOgogICAgICAtIGJ1bmRsZQogICAgICAtIGV4ZWMKICAgICAgLSBzaWRla2lxCiAgICAgIC0gJy1DJwogICAgICAtIGNvbmZpZy9zaWRla2lxLnltbAogICAgdm9sdW1lczoKICAgICAgLSAnc2lkZWtpcS1kYXRhOi9hcHAvc3RvcmFnZScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAiYnVuZGxlIGV4ZWMgcmFpbHMgcnVubmVyICdwdXRzIFNpZGVraXEucmVkaXMoJjppbmZvKScgPiAvZGV2L251bGwgMj4mMSIKICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwogIHBvc3RncmVzOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxMicKICAgIHJlc3RhcnQ6IGFsd2F5cwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXMtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19EQj1jaGF0d29vdAogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFU19VU0VSCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkU0VSVklDRV9VU0VSX1BPU1RHUkVTX1VTRVIgLWQgY2hhdHdvb3QgLWggMTI3LjAuMC4xJwogICAgICBpbnRlcnZhbDogMzBzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiA1CiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOmFscGluZScKICAgIHJlc3RhcnQ6IGFsd2F5cwogICAgY29tbWFuZDoKICAgICAgLSBzaAogICAgICAtICctYycKICAgICAgLSAncmVkaXMtc2VydmVyIC0tcmVxdWlyZXBhc3MgIiRTRVJWSUNFX1BBU1NXT1JEX1JFRElTIicKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3JlZGlzLWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSAnLWEnCiAgICAgICAgLSAkU0VSVklDRV9QQVNTV09SRF9SRURJUwogICAgICAgIC0gUElORwogICAgICBpbnRlcnZhbDogMzBzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiA1Cg==","tags":["chatwoot","chat","api","open","source","rails","redis","postgresql","sidekiq"],"logo":"svgs\/chatwoot.svg","minversion":"0.0.0","port":"3000"},"classicpress-with-mariadb":{"documentation":"https:\/\/www.classicpress.net\/?utm_source=coolify.io","slogan":"A lightweight, stable, instantly familiar free open-source content management system, based on WordPress without the block editor (Gutenberg).","compose":"c2VydmljZXM6CiAgY2xhc3NpY3ByZXNzOgogICAgaW1hZ2U6ICdjbGFzc2ljcHJlc3MvY2xhc3NpY3ByZXNzOmxhdGVzdCcKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2NsYXNzaWNwcmVzcy1maWxlczovdmFyL3d3dy9odG1sJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NMQVNTSUNQUkVTUwogICAgICAtIENMQVNTSUNQUkVTU19EQl9IT1NUPW1hcmlhZGIKICAgICAgLSBDTEFTU0lDUFJFU1NfREJfVVNFUj0kU0VSVklDRV9VU0VSX0NMQVNTSUNQUkVTUwogICAgICAtIENMQVNTSUNQUkVTU19EQl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9DTEFTU0lDUFJFU1MKICAgICAgLSBDTEFTU0lDUFJFU1NfREJfTkFNRT1jbGFzc2ljcHJlc3MKICAgIGRlcGVuZHNfb246CiAgICAgIC0gbWFyaWFkYgogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDEwCiAgbWFyaWFkYjoKICAgIGltYWdlOiAnbWFyaWFkYjoxMScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ21hcmlhZGItZGF0YTovdmFyL2xpYi9teXNxbCcKICAgIGVudmlyb25tZW50OgogICAgICAtIE1ZU1FMX1JPT1RfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUk9PVAogICAgICAtIE1ZU1FMX0RBVEFCQVNFPWNsYXNzaWNwcmVzcwogICAgICAtIE1ZU1FMX1VTRVI9JFNFUlZJQ0VfVVNFUl9DTEFTU0lDUFJFU1MKICAgICAgLSBNWVNRTF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9DTEFTU0lDUFJFU1MKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBoZWFsdGhjaGVjay5zaAogICAgICAgIC0gJy0tY29ubmVjdCcKICAgICAgICAtICctLWlubm9kYl9pbml0aWFsaXplZCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["cms","blog","content","management"],"logo":"svgs\/classicpress.svg","minversion":"0.0.0"},"classicpress-with-mysql":{"documentation":"https:\/\/www.classicpress.net\/?utm_source=coolify.io","slogan":"A lightweight, stable, instantly familiar free open-source content management system, based on WordPress without the block editor (Gutenberg).","compose":"c2VydmljZXM6CiAgY2xhc3NpY3ByZXNzOgogICAgaW1hZ2U6ICdjbGFzc2ljcHJlc3MvY2xhc3NpY3ByZXNzOmxhdGVzdCcKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2NsYXNzaWNwcmVzcy1maWxlczovdmFyL3d3dy9odG1sJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NMQVNTSUNQUkVTUwogICAgICAtIENMQVNTSUNQUkVTU19EQl9IT1NUPW15c3FsCiAgICAgIC0gQ0xBU1NJQ1BSRVNTX0RCX1VTRVI9JFNFUlZJQ0VfVVNFUl9DTEFTU0lDUFJFU1MKICAgICAgLSBDTEFTU0lDUFJFU1NfREJfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfQ0xBU1NJQ1BSRVNTCiAgICAgIC0gQ0xBU1NJQ1BSRVNTX0RCX05BTUU9Y2xhc3NpY3ByZXNzCiAgICBkZXBlbmRzX29uOgogICAgICAtIG15c3FsCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjEnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTAKICBtYXJpYWRiOgogICAgaW1hZ2U6ICdteXNxbDo1LjcnCiAgICB2b2x1bWVzOgogICAgICAtICdteXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTVlTUUxfUk9PVF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9ST09UCiAgICAgIC0gTVlTUUxfREFUQUJBU0U9Y2xhc3NpY3ByZXNzCiAgICAgIC0gTVlTUUxfVVNFUj0kU0VSVklDRV9VU0VSX0NMQVNTSUNQUkVTUwogICAgICAtIE1ZU1FMX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX0NMQVNTSUNQUkVTUwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIG15c3FsYWRtaW4KICAgICAgICAtIHBpbmcKICAgICAgICAtICctaCcKICAgICAgICAtIDEyNy4wLjAuMQogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["cms","blog","content","management"],"logo":"svgs\/classicpress.svg","minversion":"0.0.0"},"classicpress-without-database":{"documentation":"https:\/\/www.classicpress.net\/?utm_source=coolify.io","slogan":"A lightweight, stable, instantly familiar free open-source content management system, based on WordPress without the block editor (Gutenberg).","compose":"c2VydmljZXM6CiAgY2xhc3NpY3ByZXNzOgogICAgaW1hZ2U6ICdjbGFzc2ljcHJlc3MvY2xhc3NpY3ByZXNzOmxhdGVzdCcKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2NsYXNzaWNwcmVzcy1maWxlczovdmFyL3d3dy9odG1sJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NMQVNTSUNQUkVTUwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["cms","blog","content","management"],"logo":"svgs\/classicpress.svg","minversion":"0.0.0"},"cloudflared":{"documentation":"https:\/\/developers.cloudflare.com\/cloudflare-one\/connections\/connect-networks\/?utm_source=coolify.io","slogan":"Client for Cloudflare Tunnel, a daemon that exposes private services through the Cloudflare edge.","compose":"c2VydmljZXM6CiAgY2xvdWRmbGFyZWQ6CiAgICBjb250YWluZXJfbmFtZTogY2xvdWRmbGFyZS10dW5uZWwKICAgIGltYWdlOiAnY2xvdWRmbGFyZS9jbG91ZGZsYXJlZDpsYXRlc3QnCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgY29tbWFuZDogJ3R1bm5lbCBydW4nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBUVU5ORUxfVE9LRU49JENMT1VERkxBUkVfVFVOTkVMX1RPS0VOCg==","tags":null,"logo":"svgs\/cloudflared.svg","minversion":"0.0.0"},"code-server":{"documentation":"https:\/\/coder.com\/docs\/code-server\/latest?utm_source=coolify.io","slogan":"Code-Server is a web-based code editor that enables remote coding and collaboration from any device, anywhere.","compose":"c2VydmljZXM6CiAgY29kZS1zZXJ2ZXI6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvY29kZS1zZXJ2ZXI6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NPREVTRVJWRVJfODQ0MwogICAgICAtIFBVSUQ9MTAwMAogICAgICAtIFBHSUQ9MTAwMAogICAgICAtIFRaPUV1cm9wZS9NYWRyaWQKICAgICAgLSBQQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF82NF9QQVNTV09SRENPREVTRVJWRVIKICAgICAgLSBTVURPX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1NVRE9DT0RFU0VSVkVSCiAgICAgIC0gREVGQVVMVF9XT1JLU1BBQ0U9L2NvbmZpZy93b3Jrc3BhY2UKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2NvZGUtc2VydmVyLWNvbmZpZzovY29uZmlnJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjg0NDMnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["code","editor","remote","collaboration"],"logo":"svgs\/code-server.svg","minversion":"0.0.0","port":"8443"},"dashboard":{"documentation":"https:\/\/github.com\/phntxx\/dashboard?tab=readme-ov-file#dashboard?utm_source=coolify.io","slogan":"A dashboard, inspired by SUI.","compose":"c2VydmljZXM6CiAgZGFzaGJvYXJkOgogICAgaW1hZ2U6ICdwaG50eHgvZGFzaGJvYXJkOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9EQVNIQk9BUkRfODA4MAogICAgdm9sdW1lczoKICAgICAgLSAnZGFzaGJvYXJkLWRhdGE6L2FwcC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["dashboard","web","search","bookmarks"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"8080"},"directus-with-postgresql":{"documentation":"https:\/\/directus.io?utm_source=coolify.io","slogan":"Directus wraps databases with a dynamic API, and provides an intuitive app for managing its content.","compose":"c2VydmljZXM6CiAgZGlyZWN0dXM6CiAgICBpbWFnZTogJ2RpcmVjdHVzL2RpcmVjdHVzOjEwJwogICAgdm9sdW1lczoKICAgICAgLSAnZGlyZWN0dXMtdXBsb2FkczovZGlyZWN0dXMvdXBsb2FkcycKICAgICAgLSAnZGlyZWN0dXMtZXh0ZW5zaW9uczovZGlyZWN0dXMvZXh0ZW5zaW9ucycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9ESVJFQ1RVU184MDU1CiAgICAgIC0gS0VZPSRTRVJWSUNFX0JBU0U2NF82NF9LRVkKICAgICAgLSBTRUNSRVQ9JFNFUlZJQ0VfQkFTRTY0XzY0X1NFQ1JFVAogICAgICAtICdBRE1JTl9FTUFJTD0ke0FETUlOX0VNQUlMOi1hZG1pbkBleGFtcGxlLmNvbX0nCiAgICAgIC0gQURNSU5fUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfQURNSU4KICAgICAgLSBEQl9DTElFTlQ9cG9zdGdyZXMKICAgICAgLSBEQl9IT1NUPXBvc3RncmVzcWwKICAgICAgLSBEQl9QT1JUPTU0MzIKICAgICAgLSAnREJfREFUQUJBU0U9JHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1kaXJlY3R1c30nCiAgICAgIC0gREJfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTUUwKICAgICAgLSBEQl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMCiAgICAgIC0gUkVESVNfSE9TVD1yZWRpcwogICAgICAtIFJFRElTX1BPUlQ9NjM3OQogICAgICAtIFdFQlNPQ0tFVFNfRU5BQkxFRD10cnVlCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gd2dldAogICAgICAgIC0gJy1xJwogICAgICAgIC0gJy0tc3BpZGVyJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODA1NS9hZG1pbi9sb2dpbicKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICByZWRpczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2RpcmVjdHVzLXBvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWRpcmVjdHVzfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6Ny1hbHBpbmUnCiAgICBjb21tYW5kOiAncmVkaXMtc2VydmVyIC0tYXBwZW5kb25seSB5ZXMnCiAgICB2b2x1bWVzOgogICAgICAtICdkaXJlY3R1cy1yZWRpcy1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["directus","cms","database","sql"],"logo":"svgs\/directus.svg","minversion":"0.0.0","port":"8055"},"directus":{"documentation":"https:\/\/directus.io?utm_source=coolify.io","slogan":"Directus wraps databases with a dynamic API, and provides an intuitive app for managing its content.","compose":"c2VydmljZXM6CiAgZGlyZWN0dXM6CiAgICBpbWFnZTogJ2RpcmVjdHVzL2RpcmVjdHVzOjEwJwogICAgdm9sdW1lczoKICAgICAgLSAnZGlyZWN0dXMtdXBsb2FkczovZGlyZWN0dXMvdXBsb2FkcycKICAgICAgLSAnZGlyZWN0dXMtZGF0YWJhc2U6L2RpcmVjdHVzL2RhdGFiYXNlJwogICAgICAtICdkaXJlY3R1cy1leHRlbnNpb25zOi9kaXJlY3R1cy9leHRlbnNpb25zJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0RJUkVDVFVTXzgwNTUKICAgICAgLSBLRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X0tFWQogICAgICAtIFNFQ1JFVD0kU0VSVklDRV9CQVNFNjRfNjRfU0VDUkVUCiAgICAgIC0gJ0FETUlOX0VNQUlMPSR7QURNSU5fRU1BSUw6LWFkbWluQGV4YW1wbGUuY29tfScKICAgICAgLSBBRE1JTl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9BRE1JTgogICAgICAtIERCX0NMSUVOVD1zcWxpdGUzCiAgICAgIC0gREJfRklMRU5BTUU9L2RpcmVjdHVzL2RhdGFiYXNlL2RhdGEuZGIKICAgICAgLSBXRUJTT0NLRVRTX0VOQUJMRUQ9dHJ1ZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHdnZXQKICAgICAgICAtICctcScKICAgICAgICAtICctLXNwaWRlcicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwNTUvYWRtaW4vbG9naW4nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["directus","cms","database","sql"],"logo":"svgs\/directus.svg","minversion":"0.0.0","port":"8055"},"docker-registry":{"documentation":"https:\/\/docs.docker.com\/registry\/?utm_source=coolify.io","slogan":"The Docker Registry is lets you distribute Docker images.","compose":"c2VydmljZXM6CiAgcmVnaXN0cnk6CiAgICBpbWFnZTogJ3JlZ2lzdHJ5OjInCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fUkVHSVNUUllfNTAwMAogICAgICAtIFJFR0lTVFJZX0FVVEg9aHRwYXNzd2QKICAgICAgLSBSRUdJU1RSWV9BVVRIX0hUUEFTU1dEX1JFQUxNPVJlZ2lzdHJ5CiAgICAgIC0gUkVHSVNUUllfQVVUSF9IVFBBU1NXRF9QQVRIPS9hdXRoL3JlZ2lzdHJ5LnBhc3N3b3JkCiAgICAgIC0gUkVHSVNUUllfU1RPUkFHRV9GSUxFU1lTVEVNX1JPT1RESVJFQ1RPUlk9L2RhdGEKICAgIHZvbHVtZXM6CiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2F1dGgvcmVnaXN0cnkucGFzc3dvcmQKICAgICAgICB0YXJnZXQ6IC9hdXRoL3JlZ2lzdHJ5LnBhc3N3b3JkCiAgICAgICAgaXNEaXJlY3Rvcnk6IGZhbHNlCiAgICAgICAgY29udGVudDogJ3Rlc3R1c2VyOiQyeSQwNSQvbzJKdm1JMmJoRXhYSXQ2T3F4YTdla1lCN3Yzc2NqMXdGRWY2dEJzbEp2Sk9Nb1BRTC5HeScKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vY29uZmlnL2NvbmZpZy55bWwKICAgICAgICB0YXJnZXQ6IC9ldGMvZG9ja2VyL3JlZ2lzdHJ5L2NvbmZpZy55bWwKICAgICAgICBpc0RpcmVjdG9yeTogZmFsc2UKICAgICAgICBjb250ZW50OiAidmVyc2lvbjogMC4xXG5sb2c6XG4gIGZpZWxkczpcbiAgICBzZXJ2aWNlOiByZWdpc3RyeVxuc3RvcmFnZTpcbiAgY2FjaGU6XG4gICAgYmxvYmRlc2NyaXB0b3I6IGlubWVtb3J5XG4gIGZpbGVzeXN0ZW06XG4gICAgcm9vdGRpcmVjdG9yeTogL3Zhci9saWIvcmVnaXN0cnlcbmh0dHA6XG4gIGFkZHI6IDo1MDAwXG4gIGhlYWRlcnM6XG4gICAgWC1Db250ZW50LVR5cGUtT3B0aW9uczogW25vc25pZmZdXG5oZWFsdGg6XG4gIHN0b3JhZ2Vkcml2ZXI6XG4gICAgZW5hYmxlZDogdHJ1ZVxuICAgIGludGVydmFsOiAxMHNcbiAgICB0aHJlc2hvbGQ6IDMiCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2RhdGEKICAgICAgICB0YXJnZXQ6IC9kYXRhCiAgICAgICAgaXNEaXJlY3Rvcnk6IHRydWUK","tags":["registry","images","docker"],"logo":"svgs\/docker-registry.png","minversion":"0.0.0","port":"5000"},"docuseal-with-postgres":{"documentation":"https:\/\/www.docuseal.co\/?utm_source=coolify.io","slogan":"Document Signing for Everyone free forever for individuals, extensible for businesses and developers. Open Source Alternative to DocuSign, PandaDoc and more.","compose":"c2VydmljZXM6CiAgZG9jdXNlYWw6CiAgICBpbWFnZTogJ2RvY3VzZWFsL2RvY3VzZWFsOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9ET0NVU0VBTF8zMDAwCiAgICAgIC0gJ0hPU1Q9JHtTRVJWSUNFX0ZRRE5fRE9DVVNFQUx9JwogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXNxbDovLyRTRVJWSUNFX1VTRVJfUE9TVEdSRVM6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNAcG9zdGdyZXNxbDo1NDMyLyR7UE9TVEdSRVNfREJ9JwogICAgdm9sdW1lczoKICAgICAgLSAnZG9jdXNlYWwtZGF0YTovZGF0YScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLXEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWRvY3VzZWFsfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["documentation"],"logo":"svgs\/docuseal.png","minversion":"0.0.0","port":"3000"},"docuseal":{"documentation":"https:\/\/www.docuseal.co\/?utm_source=coolify.io","slogan":"Document Signing for Everyone free forever for individuals, extensible for businesses and developers. Open Source Alternative to DocuSign, PandaDoc and more.","compose":"c2VydmljZXM6CiAgZG9jdXNlYWw6CiAgICBpbWFnZTogJ2RvY3VzZWFsL2RvY3VzZWFsOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9ET0NVU0VBTF8zMDAwCiAgICAgIC0gJ0hPU1Q9JHtTRVJWSUNFX0ZRRE5fRE9DVVNFQUx9JwogICAgdm9sdW1lczoKICAgICAgLSAnZG9jdXNlYWwtZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLXEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["documentation"],"logo":"svgs\/docuseal.png","minversion":"0.0.0","port":"3000"},"dokuwiki":{"documentation":"https:\/\/www.dokuwiki.org\/?utm_source=coolify.io","slogan":"A lightweight and easy-to-use wiki platform for creating and managing documentation and knowledge bases.","compose":"c2VydmljZXM6CiAgZG9rdXdpa2k6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvZG9rdXdpa2k6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0RPS1VXSUtJCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZG9rdXdpa2ktY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["wiki","documentation","knowledge","base"],"logo":"svgs\/dokuwiki.png","minversion":"0.0.0"},"duplicati":{"documentation":"https:\/\/duplicati.readthedocs.io?utm_source=coolify.io","slogan":"Duplicati is a backup solution, allowing you to make scheduled backups with encryption.","compose":"c2VydmljZXM6CiAgZHVwbGljYXRpOgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL2R1cGxpY2F0aTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fRFVQTElDQVRJXzgyMDAKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICB2b2x1bWVzOgogICAgICAtICdkdXBsaWNhdGktY29uZmlnOi9jb25maWcnCiAgICAgIC0gJ2R1cGxpY2F0aS1iYWNrdXBzOi9iYWNrdXBzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgyMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["backup","encryption"],"logo":"svgs\/duplicati.webp","minversion":"0.0.0","port":"8200"},"emby":{"documentation":"https:\/\/emby.media\/support\/articles\/Home.html?utm_source=coolify.io","slogan":"A media server software that allows you to organize, stream, and access your multimedia content effortlessly.","compose":"c2VydmljZXM6CiAgZW1ieToKICAgIGltYWdlOiAnbHNjci5pby9saW51eHNlcnZlci9lbWJ5OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9FTUJZXzgwOTYKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICB2b2x1bWVzOgogICAgICAtICdlbWJ5LWNvbmZpZzovY29uZmlnJwogICAgICAtICdlbWJ5LXR2c2hvd3M6L3R2c2hvd3MnCiAgICAgIC0gJ2VtYnktbW92aWVzOi9tb3ZpZXMnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODA5NicKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=","tags":["media","server","movies","tv","music"],"logo":"svgs\/emby.png","minversion":"0.0.0","port":"8096"},"embystat":{"documentation":"https:\/\/github.com\/mregni\/EmbyStat?utm_source=coolify.io","slogan":"EmnyStat is a web analytics tool, designed to provide insight into website traffic and user behavior.","compose":"c2VydmljZXM6CiAgZW1ieXN0YXQ6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvZW1ieXN0YXQ6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0VNQllTVEFUXzY1NTUKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICB2b2x1bWVzOgogICAgICAtICdlbWJ5c3RhdC1jb25maWc6L2NvbmZpZycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo2NTU1JwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==","tags":["media","server","movies","tv","music"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"6555"},"fider":{"documentation":"https:\/\/fider.io?utm_source=coolify.io","slogan":"Fider is a feedback platform for collecting and managing user feedback.","compose":"c2VydmljZXM6CiAgZmlkZXI6CiAgICBpbWFnZTogJ2dldGZpZGVyL2ZpZGVyOnN0YWJsZScKICAgIGVudmlyb25tZW50OgogICAgICBCQVNFX1VSTDogJFNFUlZJQ0VfRlFETl9GSURFUl8zMDAwCiAgICAgIERBVEFCQVNFX1VSTDogJ3Bvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFUzokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0BkYXRhYmFzZTo1NDMyL2ZpZGVyP3NzbG1vZGU9ZGlzYWJsZScKICAgICAgSldUX1NFQ1JFVDogJFNFUlZJQ0VfUEFTU1dPUkRfNjRfRklERVIKICAgICAgRU1BSUxfTk9SRVBMWTogJyR7RU1BSUxfTk9SRVBMWTotbm9yZXBseUBleGFtcGxlLmNvbX0nCiAgICAgIEVNQUlMX01BSUxHVU5fQVBJOiAkRU1BSUxfTUFJTEdVTl9BUEkKICAgICAgRU1BSUxfTUFJTEdVTl9ET01BSU46ICRFTUFJTF9NQUlMR1VOX0RPTUFJTgogICAgICBFTUFJTF9NQUlMR1VOX1JFR0lPTjogJEVNQUlMX01BSUxHVU5fUkVHSU9OCiAgICAgIEVNQUlMX1NNVFBfSE9TVDogJyR7RU1BSUxfU01UUF9IT1NUOi1zbXRwLm1haWxndW4uY29tfScKICAgICAgRU1BSUxfU01UUF9QT1JUOiAnJHtFTUFJTF9TTVRQX1BPUlQ6LTU4N30nCiAgICAgIEVNQUlMX1NNVFBfVVNFUk5BTUU6ICcke0VNQUlMX1NNVFBfVVNFUk5BTUU6LXBvc3RtYXN0ZXJAbWFpbGd1bi5jb219JwogICAgICBFTUFJTF9TTVRQX1BBU1NXT1JEOiAkRU1BSUxfU01UUF9QQVNTV09SRAogICAgICBFTUFJTF9TTVRQX0VOQUJMRV9TVEFSVFRMUzogJEVNQUlMX1NNVFBfRU5BQkxFX1NUQVJUVExTCiAgICAgIEVNQUlMX0FXU1NFU19SRUdJT046ICRFTUFJTF9BV1NTRVNfUkVHSU9OCiAgICAgIEVNQUlMX0FXU1NFU19BQ0NFU1NfS0VZX0lEOiAkRU1BSUxfQVdTU0VTX0FDQ0VTU19LRVlfSUQKICAgICAgRU1BSUxfQVdTU0VTX1NFQ1JFVF9BQ0NFU1NfS0VZOiAkRU1BSUxfQVdTU0VTX1NFQ1JFVF9BQ0NFU1NfS0VZCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gL2FwcC9maWRlcgogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDEwCiAgZGF0YWJhc2U6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjEyJwogICAgdm9sdW1lczoKICAgICAgLSAncGdfZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgUE9TVEdSRVNfVVNFUjogJFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICBQT1NUR1JFU19QQVNTV09SRDogJFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgUE9TVEdSRVNfREI6ICcke1BPU1RHUkVTX0RCOi1maWRlcn0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcGdfaXNyZWFkeQogICAgICAgIC0gJy1VJwogICAgICAgIC0gJFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["feedback","user-feedback"],"logo":"svgs\/fider.svg","minversion":"0.0.0","port":"3000"},"filebrowser":{"documentation":"https:\/\/filebrowser.org?utm_source=coolify.io","slogan":"FileBrowser is a web-based file manager and file explorer with a user-friendly interface.","compose":"c2VydmljZXM6CiAgZmlsZWJyb3dzZXI6CiAgICBpbWFnZTogJ2ZpbGVicm93c2VyL2ZpbGVicm93c2VyOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9GSUxFQlJPV1NFUgogICAgdm9sdW1lczoKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vc3J2CiAgICAgICAgdGFyZ2V0OiAvc3J2CiAgICAgICAgaXNEaXJlY3Rvcnk6IHRydWUKICAgICAgLSAnLi9kYXRhYmFzZS5kYjovZGF0YWJhc2UuZGInCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2ZpbGVicm93c2VyLmpzb24KICAgICAgICB0YXJnZXQ6IC8uZmlsZWJyb3dzZXIuanNvbgogICAgICAgIHJlYWRfb25seTogdHJ1ZQogICAgICAgIGNvbnRlbnQ6ICd7fScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=","tags":["file-management","storage-access","data-organization","file-utilization","administration-tool"],"logo":"svgs\/filebrowser.svg","minversion":"0.0.0"},"firefly":{"documentation":"https:\/\/firefly-iii.org?utm_source=coolify.io","slogan":"A personal finances manager that can help you save money.","compose":"c2VydmljZXM6CiAgZmlyZWZseToKICAgIGltYWdlOiAnZmlyZWZseWlpaS9jb3JlOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9GSVJFRkxZXzgwODAKICAgICAgLSBBUFBfS0VZPSRTRVJWSUNFX0JBU0U2NF9BUFBLRVkKICAgICAgLSBEQl9IT1NUPW15c3FsCiAgICAgIC0gREJfUE9SVD0zMzA2CiAgICAgIC0gREJfQ09OTkVDVElPTj1teXNxbAogICAgICAtICdEQl9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFOi1maXJlZmx5fScKICAgICAgLSBEQl9VU0VSTkFNRT0kU0VSVklDRV9VU0VSX01ZU1FMCiAgICAgIC0gREJfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfTVlTUUwKICAgICAgLSBTVEFUSUNfQ1JPTl9UT0tFTj0kU0VSVklDRV9CQVNFNjRfQ1JPTlRPS0VOCiAgICAgIC0gJ1RSVVNURURfUFJPWElFUz0qJwogICAgdm9sdW1lczoKICAgICAgLSAnZmlyZWZseS11cGxvYWQ6L3Zhci93d3cvaHRtbC9zdG9yYWdlL3VwbG9hZCcKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MDgwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgICBkZXBlbmRzX29uOgogICAgICBteXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogIG15c3FsOgogICAgaW1hZ2U6ICdtYXJpYWRiOmx0cycKICAgIGVudmlyb25tZW50OgogICAgICAtICdNWVNRTF9VU0VSPSR7U0VSVklDRV9VU0VSX01ZU1FMfScKICAgICAgLSAnTVlTUUxfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMfScKICAgICAgLSAnTVlTUUxfREFUQUJBU0U9JHtNWVNRTF9EQVRBQkFTRTotZmlyZWZseX0nCiAgICAgIC0gJ01ZU1FMX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMUk9PVH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gbWFyaWFkYi1hZG1pbgogICAgICAgIC0gcGluZwogICAgICAgIC0gJy1oJwogICAgICAgIC0gMTI3LjAuMC4xCiAgICAgICAgLSAnLXVyb290JwogICAgICAgIC0gJy1wJHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMUk9PVH0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2ZpcmVmbHktbXlzcWwtZGF0YTovdmFyL2xpYi9teXNxbCcKICBjcm9uOgogICAgaW1hZ2U6IGFscGluZQogICAgZW50cnlwb2ludDoKICAgICAgLSAvZW50cnlwb2ludC5zaAogICAgdm9sdW1lczoKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vZW50cnlwb2ludC5zaAogICAgICAgIHRhcmdldDogL2VudHJ5cG9pbnQuc2gKICAgICAgICBjb250ZW50OiAiIyEvYmluL3NoXG4jIFN1YnN0aXR1dGUgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlIGludG8gdGhlIGNyb24gY29tbWFuZFxuQ1JPTl9DT01NQU5EPVwiMCAzICogKiAqIHdnZXQgLXFPLSBodHRwOi8vZmlyZWZseTo4MDgwL2FwaS92MS9jcm9uLyR7U1RBVElDX0NST05fVE9LRU59XCJcbiMgQWRkIHRoZSBjcm9uIGNvbW1hbmQgdG8gdGhlIGNyb250YWJcbmVjaG8gXCIkQ1JPTl9DT01NQU5EXCIgfCBjcm9udGFiIC1cbiMgU3RhcnQgdGhlIGNyb24gZGFlbW9uIGluIHRoZSBmb3JlZ3JvdW5kIHdpdGggbG9nZ2luZyB0byBzdGRvdXRcbmNyb25kIC1mIC1MIC9kZXYvc3Rkb3V0IgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU1RBVElDX0NST05fVE9LRU49JFNFUlZJQ0VfQkFTRTY0X0NST05UT0tFTgo=","tags":["finance","money","personal","manager"],"logo":"svgs\/firefly.svg","minversion":"0.0.0","port":"8080"},"formbricks":{"documentation":"https:\/\/formbricks.com?utm_source=coolify.io","slogan":"Open Source Experience Management","compose":"c2VydmljZXM6CiAgZm9ybWJyaWNrczoKICAgIGltYWdlOiAnZ2hjci5pby9mb3JtYnJpY2tzL2Zvcm1icmlja3M6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0ZPUk1CUklDS1NfMzAwMAogICAgICAtIFdFQkFQUF9VUkw9JFNFUlZJQ0VfRlFETl9GT1JNQlJJQ0tTCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyRTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTDokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMQHBvc3RncmVzcWw6NTQzMi8ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWZvcm1icmlja3N9JwogICAgICAtIE5FWFRBVVRIX1NFQ1JFVD0kU0VSVklDRV9CQVNFNjRfNjRfTkVYVEFVVEgKICAgICAgLSBORVhUQVVUSF9VUkw9JFNFUlZJQ0VfRlFETl9GT1JNQlJJQ0tTCiAgICAgIC0gRU5DUllQVElPTl9LRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X0VOQ1JZUFRJT04KICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdNQUlMX0ZST009JHtNQUlMX0ZST006LXRlc3RAZXhhbXBsZS5jb219JwogICAgICAtICdTTVRQX0hPU1Q9JHtTTVRQX0hPU1Q6LXRlc3QuZXhhbXBsZS5jb219JwogICAgICAtICdTTVRQX1BPUlQ9JHtTTVRQX1BPUlQ6LTU4N30nCiAgICAgIC0gJ1NNVFBfVVNFUj0ke1NNVFBfVVNFUjotdGVzdH0nCiAgICAgIC0gJ1NNVFBfUEFTU1dPUkQ9JHtTTVRQX1BBU1NXT1JEOi10ZXN0fScKICAgICAgLSAnU01UUF9TRUNVUkVfRU5BQkxFRD0ke1NNVFBfU0VDVVJFX0VOQUJMRUQ6LTB9JwogICAgICAtICdTSE9SVF9VUkxfQkFTRT0ke1NIT1JUX1VSTF9CQVNFfScKICAgICAgLSAnRU1BSUxfVkVSSUZJQ0FUSU9OX0RJU0FCTEVEPSR7RU1BSUxfVkVSSUZJQ0FUSU9OX0RJU0FCTEVEOi0xfScKICAgICAgLSAnUEFTU1dPUkRfUkVTRVRfRElTQUJMRUQ9JHtQQVNTV09SRF9SRVNFVF9ESVNBQkxFRDotMX0nCiAgICAgIC0gJ1NJR05VUF9ESVNBQkxFRD0ke1NJR05VUF9ESVNBQkxFRDotMH0nCiAgICAgIC0gJ0lOVklURV9ESVNBQkxFRD0ke0lOVklURV9ESVNBQkxFRDotMH0nCiAgICAgIC0gJ1BSSVZBQ1lfVVJMPSR7UFJJVkFDWV9VUkx9JwogICAgICAtICdURVJNU19VUkw9JHtURVJNU19VUkx9JwogICAgICAtICdJTVBSSU5UX1VSTD0ke0lNUFJJTlRfVVJMfScKICAgICAgLSAnR0lUSFVCX0FVVEhfRU5BQkxFRD0ke0dJVEhVQl9BVVRIX0VOQUJMRUQ6LTB9JwogICAgICAtICdHSVRIVUJfSUQ9JHtHSVRIVUJfSUR9JwogICAgICAtICdHSVRIVUJfU0VDUkVUPSR7R0lUSFVCX1NFQ1JFVH0nCiAgICAgIC0gJ0dPT0dMRV9BVVRIX0VOQUJMRUQ9JHtHT09HTEVfQVVUSF9FTkFCTEVEOi0wfScKICAgICAgLSAnR09PR0xFX0NMSUVOVF9JRD0ke0dPT0dMRV9DTElFTlRfSUR9JwogICAgICAtICdHT09HTEVfQ0xJRU5UX1NFQ1JFVD0ke0dPT0dMRV9DTElFTlRfU0VDUkVUfScKICAgICAgLSAnQVNTRVRfUFJFRklYX1VSTD0ke0FTU0VUX1BSRUZJWF9VUkx9JwogICAgdm9sdW1lczoKICAgICAgLSAnZm9ybWJyaWNrcy11cGxvYWRzOi9hcHBzL3dlYi91cGxvYWRzLycKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAnZm9ybWJyaWNrcy1wb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1mb3JtYnJpY2tzfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["form","builder","forms","open source","experience","management","self-hosted","docker"],"logo":"svgs\/formbricks.png","minversion":"0.0.0","port":"3000"},"ghost":{"documentation":"https:\/\/ghost.org?utm_source=coolify.io","slogan":"Ghost is a content management system (CMS) and blogging platform.","compose":"c2VydmljZXM6CiAgZ2hvc3Q6CiAgICBpbWFnZTogJ2dob3N0OjUnCiAgICB2b2x1bWVzOgogICAgICAtICdnaG9zdC1jb250ZW50LWRhdGE6L3Zhci9saWIvZ2hvc3QvY29udGVudCcKICAgIGVudmlyb25tZW50OgogICAgICAtIHVybD0kU0VSVklDRV9GUUROX0dIT1NUXzIzNjgKICAgICAgLSBkYXRhYmFzZV9fY2xpZW50PW15c3FsCiAgICAgIC0gZGF0YWJhc2VfX2Nvbm5lY3Rpb25fX2hvc3Q9bXlzcWwKICAgICAgLSBkYXRhYmFzZV9fY29ubmVjdGlvbl9fdXNlcj0kU0VSVklDRV9VU0VSX01ZU1FMCiAgICAgIC0gZGF0YWJhc2VfX2Nvbm5lY3Rpb25fX3Bhc3N3b3JkPSRTRVJWSUNFX1BBU1NXT1JEX01ZU1FMCiAgICAgIC0gJ2RhdGFiYXNlX19jb25uZWN0aW9uX19kYXRhYmFzZT0ke01ZU1FMX0RBVEFCQVNFLWdob3N0fScKICAgICAgLSBtYWlsX190cmFuc3BvcnQ9U01UUAogICAgICAtICdtYWlsX19vcHRpb25zX19hdXRoX19wYXNzPSR7TUFJTF9PUFRJT05TX0FVVEhfUEFTU30nCiAgICAgIC0gJ21haWxfX29wdGlvbnNfX2F1dGhfX3VzZXI9JHtNQUlMX09QVElPTlNfQVVUSF9VU0VSfScKICAgICAgLSAnbWFpbF9fb3B0aW9uc19fc2VjdXJlPSR7TUFJTF9PUFRJT05TX1NFQ1VSRTotdHJ1ZX0nCiAgICAgIC0gJ21haWxfX29wdGlvbnNfX3BvcnQ9JHtNQUlMX09QVElPTlNfUE9SVDotNDY1fScKICAgICAgLSAnbWFpbF9fb3B0aW9uc19fc2VydmljZT0ke01BSUxfT1BUSU9OU19TRVJWSUNFOi1NYWlsZ3VufScKICAgICAgLSAnbWFpbF9fb3B0aW9uc19faG9zdD0ke01BSUxfT1BUSU9OU19IT1NUfScKICAgIGRlcGVuZHNfb246CiAgICAgIG15c3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gZWNobwogICAgICAgIC0gb2sKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo4LjAnCiAgICB2b2x1bWVzOgogICAgICAtICdnaG9zdC1teXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtTRVJWSUNFX1VTRVJfTVlTUUx9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUx9JwogICAgICAtICdNWVNRTF9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFfScKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUxST09UfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBteXNxbGFkbWluCiAgICAgICAgLSBwaW5nCiAgICAgICAgLSAnLWgnCiAgICAgICAgLSAxMjcuMC4wLjEKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["cms","blog","content","management","system"],"logo":"svgs\/ghost.svg","minversion":"0.0.0","port":"2368"},"gitea-with-mariadb":{"documentation":"https:\/\/docs.gitea.com?utm_source=coolify.io","slogan":"Gitea is a self-hosted, lightweight Git service, offering version control, collaboration, and code hosting.","compose":"c2VydmljZXM6CiAgZ2l0ZWE6CiAgICBpbWFnZTogJ2dpdGVhL2dpdGVhOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9HSVRFQV8zMDAwCiAgICAgIC0gVVNFUl9VSUQ9MTAwMAogICAgICAtIFVTRVJfR0lEPTEwMDAKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX0RCX1RZUEU9bXlzcWwKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX0hPU1Q9bWFyaWFkYgogICAgICAtICdHSVRFQV9fZGF0YWJhc2VfX05BTUU9JHtNWVNRTF9EQVRBQkFTRS1naXRlYX0nCiAgICAgIC0gR0lURUFfX2RhdGFiYXNlX19VU0VSPSRTRVJWSUNFX1VTRVJfTVlTUUwKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX1BBU1NXRD0kU0VSVklDRV9QQVNTV09SRF9NWVNRTAogICAgdm9sdW1lczoKICAgICAgLSAnZ2l0ZWEtZGF0YTovdmFyL2xpYi9naXRlYScKICAgICAgLSAnZ2l0ZWEtdGltZXpvbmU6L2V0Yy90aW1lem9uZTpybycKICAgICAgLSAnZ2l0ZWEtbG9jYWx0aW1lOi9ldGMvbG9jYWx0aW1lOnJvJwogICAgcG9ydHM6CiAgICAgIC0gJzIyMjIyOjIyJwogICAgZGVwZW5kc19vbjoKICAgICAgbWFyaWFkYjoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjMwMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICBtYXJpYWRiOgogICAgaW1hZ2U6ICdtYXJpYWRiOjExJwogICAgdm9sdW1lczoKICAgICAgLSAnZ2l0ZWEtbWFyaWFkYi1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtTRVJWSUNFX1VTRVJfTVlTUUx9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUx9JwogICAgICAtICdNWVNRTF9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFfScKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUxST09UfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBoZWFsdGhjaGVjay5zaAogICAgICAgIC0gJy0tY29ubmVjdCcKICAgICAgICAtICctLWlubm9kYl9pbml0aWFsaXplZCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["version control","collaboration","code","hosting","lightweight","mariadb"],"logo":"svgs\/gitea.svg","minversion":"0.0.0"},"gitea-with-mysql":{"documentation":"https:\/\/docs.gitea.com?utm_source=coolify.io","slogan":"Gitea is a self-hosted, lightweight Git service, offering version control, collaboration, and code hosting.","compose":"c2VydmljZXM6CiAgZ2l0ZWE6CiAgICBpbWFnZTogJ2dpdGVhL2dpdGVhOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9HSVRFQV8zMDAwCiAgICAgIC0gVVNFUl9VSUQ9MTAwMAogICAgICAtIFVTRVJfR0lEPTEwMDAKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX0RCX1RZUEU9bXlzcWwKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX0hPU1Q9bXlzcWwKICAgICAgLSAnR0lURUFfX2RhdGFiYXNlX19OQU1FPSR7TVlTUUxfREFUQUJBU0UtZ2l0ZWF9JwogICAgICAtIEdJVEVBX19kYXRhYmFzZV9fVVNFUj0kU0VSVklDRV9VU0VSX01ZU1FMCiAgICAgIC0gR0lURUFfX2RhdGFiYXNlX19QQVNTV0Q9JFNFUlZJQ0VfUEFTU1dPUkRfTVlTUUwKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2dpdGVhLWRhdGE6L3Zhci9saWIvZ2l0ZWEnCiAgICAgIC0gJ2dpdGVhLXRpbWV6b25lOi9ldGMvdGltZXpvbmU6cm8nCiAgICAgIC0gJ2dpdGVhLWxvY2FsdGltZTovZXRjL2xvY2FsdGltZTpybycKICAgIHBvcnRzOgogICAgICAtICcyMjIyMjoyMicKICAgIGRlcGVuZHNfb246CiAgICAgIG15c3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo4LjAnCiAgICB2b2x1bWVzOgogICAgICAtICdnaXRlYS1teXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtTRVJWSUNFX1VTRVJfTVlTUUx9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUx9JwogICAgICAtICdNWVNRTF9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFfScKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUxST09UfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBteXNxbGFkbWluCiAgICAgICAgLSBwaW5nCiAgICAgICAgLSAnLWgnCiAgICAgICAgLSAxMjcuMC4wLjEKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["version control","collaboration","code","hosting","lightweight","mysql"],"logo":"svgs\/gitea.svg","minversion":"0.0.0"},"gitea-with-postgresql":{"documentation":"https:\/\/docs.gitea.com?utm_source=coolify.io","slogan":"Gitea is a self-hosted, lightweight Git service, offering version control, collaboration, and code hosting.","compose":"c2VydmljZXM6CiAgZ2l0ZWE6CiAgICBpbWFnZTogJ2dpdGVhL2dpdGVhOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9HSVRFQV8zMDAwCiAgICAgIC0gVVNFUl9VSUQ9MTAwMAogICAgICAtIFVTRVJfR0lEPTEwMDAKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX0RCX1RZUEU9cG9zdGdyZXMKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX0hPU1Q9cG9zdGdyZXNxbAogICAgICAtICdHSVRFQV9fZGF0YWJhc2VfX05BTUU9JHtQT1NUR1JFU1FMX0RBVEFCQVNFLWdpdGVhfScKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMCiAgICAgIC0gR0lURUFfX2RhdGFiYXNlX19QQVNTV0Q9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTAogICAgdm9sdW1lczoKICAgICAgLSAnZ2l0ZWEtZGF0YTovdmFyL2xpYi9naXRlYScKICAgICAgLSAnZ2l0ZWEtdGltZXpvbmU6L2V0Yy90aW1lem9uZTpybycKICAgICAgLSAnZ2l0ZWEtbG9jYWx0aW1lOi9ldGMvbG9jYWx0aW1lOnJvJwogICAgcG9ydHM6CiAgICAgIC0gJzIyMjIyOjIyJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjMwMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdnaXRlYS1wb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU1FMX0RBVEFCQVNFfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["version control","collaboration","code","hosting","lightweight","postgresql"],"logo":"svgs\/gitea.svg","minversion":"0.0.0"},"gitea":{"documentation":"https:\/\/docs.gitea.com?utm_source=coolify.io","slogan":"Gitea is a self-hosted, lightweight Git service, offering version control, collaboration, and code hosting.","compose":"c2VydmljZXM6CiAgZ2l0ZWE6CiAgICBpbWFnZTogJ2dpdGVhL2dpdGVhOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9HSVRFQV8zMDAwCiAgICAgIC0gVVNFUl9VSUQ9MTAwMAogICAgICAtIFVTRVJfR0lEPTEwMDAKICAgIHBvcnRzOgogICAgICAtICcyMjIyMjoyMicKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2dpdGVhLWRhdGE6L2RhdGEnCiAgICAgIC0gJ2dpdGVhLXRpbWV6b25lOi9ldGMvdGltZXpvbmU6cm8nCiAgICAgIC0gJ2dpdGVhLWxvY2FsdGltZTovZXRjL2xvY2FsdGltZTpybycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==","tags":["version control","collaboration","code","hosting","lightweight"],"logo":"svgs\/gitea.svg","minversion":"0.0.0"},"glance":{"documentation":"https:\/\/github.com\/glanceapp\/glance?utm_source=coolify.io","slogan":"A self-hosted dashboard that puts all your feeds in one place.","compose":"c2VydmljZXM6CiAgZ2xhbmNlOgogICAgaW1hZ2U6ICdnbGFuY2VhcHAvZ2xhbmNlOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9HTEFOQ0VfODA4MAogICAgdm9sdW1lczoKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vZ2xhbmNlLXNldHRpbmdzCiAgICAgICAgdGFyZ2V0OiAvYXBwL2dsYW5jZS55bWwKICAgICAgICBjb250ZW50OiAicGFnZXM6XG4gIC0gbmFtZTogSG9tZVxuICAgIHNlcnZlcjpcbiAgICAgIGhvc3Q6IDAuMC4wLjBcbiAgICAgIHBvcnQ6IDgwODBcbiAgICAgIGFzc2V0cy1wYXRoOiAvdXNlci9hc3NldHNcbiAgICBjb2x1bW5zOlxuICAgICAgLSBzaXplOiBzbWFsbFxuICAgICAgICB3aWRnZXRzOlxuICAgICAgICAgIC0gdHlwZTogY2FsZW5kYXJcblxuICAgICAgICAgIC0gdHlwZTogcnNzXG4gICAgICAgICAgICBsaW1pdDogMTBcbiAgICAgICAgICAgIGNvbGxhcHNlLWFmdGVyOiAzXG4gICAgICAgICAgICBjYWNoZTogM2hcbiAgICAgICAgICAgIGZlZWRzOlxuICAgICAgICAgICAgICAtIHVybDogaHR0cHM6Ly9jaWVjaGFub3cuc2tpL2F0b20ueG1sXG4gICAgICAgICAgICAgIC0gdXJsOiBodHRwczovL3d3dy5qb3Nod2NvbWVhdS5jb20vcnNzLnhtbFxuICAgICAgICAgICAgICAgIHRpdGxlOiBKb3NoIENvbWVhdVxuICAgICAgICAgICAgICAtIHVybDogaHR0cHM6Ly9zYW13aG8uZGV2L3Jzcy54bWxcbiAgICAgICAgICAgICAgLSB1cmw6IGh0dHBzOi8vYXdlc29tZWtsaW5nLmdpdGh1Yi5pby9mZWVkLnhtbFxuICAgICAgICAgICAgICAtIHVybDogaHR0cHM6Ly9pc2hhZGVlZC5jb20vZmVlZC54bWxcbiAgICAgICAgICAgICAgICB0aXRsZTogQWhtYWQgU2hhZGVlZFxuXG4gICAgICAgICAgLSB0eXBlOiB0d2l0Y2gtY2hhbm5lbHNcbiAgICAgICAgICAgIGNoYW5uZWxzOlxuICAgICAgICAgICAgICAtIHRoZXByaW1lYWdlblxuICAgICAgICAgICAgICAtIGhleWFuZHJhc1xuICAgICAgICAgICAgICAtIGNvaGhjYXJuYWdlXG4gICAgICAgICAgICAgIC0gY2hyaXN0aXR1c3RlY2hcbiAgICAgICAgICAgICAgLSBibHVyYnNcbiAgICAgICAgICAgICAgLSBhc21vbmdvbGRcbiAgICAgICAgICAgICAgLSBqZW1iYXdsc1xuXG4gICAgICAtIHNpemU6IGZ1bGxcbiAgICAgICAgd2lkZ2V0czpcbiAgICAgICAgICAtIHR5cGU6IGhhY2tlci1uZXdzXG5cbiAgICAgICAgICAtIHR5cGU6IHZpZGVvc1xuICAgICAgICAgICAgY2hhbm5lbHM6XG4gICAgICAgICAgICAgIC0gVUNSLURYYzF2b292UzhuaEF2Y2NSWmhnICMgSmVmZiBHZWVybGluZ1xuICAgICAgICAgICAgICAtIFVDdjZKX2pKYThHSnFGd1FOZ05yTXV3dyAjIFNlcnZlVGhlSG9tZVxuICAgICAgICAgICAgICAtIFVDT2stZ0h5amNXWk5qM0JyNG94d2gwQSAjIFRlY2hubyBUaW1cblxuICAgICAgICAgIC0gdHlwZTogcmVkZGl0XG4gICAgICAgICAgICBzdWJyZWRkaXQ6IHNlbGZob3N0ZWRcblxuICAgICAgLSBzaXplOiBzbWFsbFxuICAgICAgICB3aWRnZXRzOlxuICAgICAgICAgIC0gdHlwZTogd2VhdGhlclxuICAgICAgICAgICAgbG9jYXRpb246IExvbmRvbiwgVW5pdGVkIEtpbmdkb21cblxuICAgICAgICAgIC0gdHlwZTogc3RvY2tzXG4gICAgICAgICAgICBzdG9ja3M6XG4gICAgICAgICAgICAgIC0gc3ltYm9sOiBTUFlcbiAgICAgICAgICAgICAgICBuYW1lOiBTJlAgNTAwXG4gICAgICAgICAgICAgIC0gc3ltYm9sOiBCVEMtVVNEXG4gICAgICAgICAgICAgICAgbmFtZTogQml0Y29pblxuICAgICAgICAgICAgICAtIHN5bWJvbDogTlZEQVxuICAgICAgICAgICAgICAgIG5hbWU6IE5WSURJQVxuICAgICAgICAgICAgICAtIHN5bWJvbDogQUFQTFxuICAgICAgICAgICAgICAgIG5hbWU6IEFwcGxlXG4gICAgICAgICAgICAgIC0gc3ltYm9sOiBNU0ZUXG4gICAgICAgICAgICAgICAgbmFtZTogTWljcm9zb2Z0XG4gICAgICAgICAgICAgIC0gc3ltYm9sOiBHT09HTFxuICAgICAgICAgICAgICAgIG5hbWU6IEdvb2dsZVxuICAgICAgICAgICAgICAtIHN5bWJvbDogQU1EXG4gICAgICAgICAgICAgICAgbmFtZTogQU1EXG4gICAgICAgICAgICAgIC0gc3ltYm9sOiBSRERUXG4gICAgICAgICAgICAgICAgbmFtZTogUmVkZGl0IgogICAgICAtICdnbGFuY2UtYXNzZXRzOi91c2VyL2Fzc2V0cycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBlY2hvCiAgICAgICAgLSAnWytdIFNob3VsZCBiZSB3b3JraW5nIGZpbmUuJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["dashboard","server","applications","interface","rrss"],"logo":"svgs\/glance.png","minversion":"0.0.0","port":"8080"},"glitchtip":{"documentation":"https:\/\/glitchtip.com?utm_source=coolify.io","slogan":"GlitchTip is a self-hosted, open-source error tracking system.","compose":"c2VydmljZXM6CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZ2xpdGNodGlwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BnLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHJlZGlzOgogICAgaW1hZ2U6IHJlZGlzCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICB3ZWI6CiAgICBpbWFnZTogZ2xpdGNodGlwL2dsaXRjaHRpcAogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3JlcwogICAgICAtIHJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fR0xJVENIVElQXzgwODAKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUxAcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWdsaXRjaHRpcH0nCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfRU5DUllQVElPTgogICAgICAtICdFTUFJTF9VUkw9JHtFTUFJTF9VUkw6LWNvbnNvbGVtYWlsOi8vfScKICAgICAgLSAnR0xJVENIVElQX0RPTUFJTj0ke1NFUlZJQ0VfRlFETl9HTElUQ0hUSVB9JwogICAgICAtICdERUZBVUxUX0ZST01fRU1BSUw9JHtERUZBVUxUX0ZST01fRU1BSUw6LXRlc3RAZXhhbXBsZS5jb219JwogICAgICAtICdDRUxFUllfV09SS0VSX0FVVE9TQ0FMRT0ke0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFOi0xLDN9JwogICAgICAtICdDRUxFUllfV09SS0VSX01BWF9UQVNLU19QRVJfQ0hJTEQ9JHtDRUxFUllfV09SS0VSX01BWF9UQVNLU19QRVJfQ0hJTEQ6LTEwMDAwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3VwbG9hZHM6L2NvZGUvdXBsb2FkcycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBlY2hvCiAgICAgICAgLSBvawogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgd29ya2VyOgogICAgaW1hZ2U6IGdsaXRjaHRpcC9nbGl0Y2h0aXAKICAgIGNvbW1hbmQ6IC4vYmluL3J1bi1jZWxlcnktd2l0aC1iZWF0LnNoCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzCiAgICAgIC0gcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTUUw6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTEBwb3N0Z3Jlczo1NDMyLyR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZ2xpdGNodGlwfScKICAgICAgLSBTRUNSRVRfS0VZPSRTRVJWSUNFX0JBU0U2NF82NF9FTkNSWVBUSU9OCiAgICAgIC0gJ0VNQUlMX1VSTD0ke0VNQUlMX1VSTDotY29uc29sZW1haWw6Ly99JwogICAgICAtICdHTElUQ0hUSVBfRE9NQUlOPSR7U0VSVklDRV9GUUROX0dMSVRDSFRJUH0nCiAgICAgIC0gJ0RFRkFVTFRfRlJPTV9FTUFJTD0ke0RFRkFVTFRfRlJPTV9FTUFJTDotdGVzdEBleGFtcGxlLmNvbX0nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFPSR7Q0VMRVJZX1dPUktFUl9BVVRPU0NBTEU6LTEsM30nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRD0ke0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRDotMTAwMDB9JwogICAgdm9sdW1lczoKICAgICAgLSAndXBsb2FkczovY29kZS91cGxvYWRzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGVjaG8KICAgICAgICAtIG9rCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICBtaWdyYXRlOgogICAgaW1hZ2U6IGdsaXRjaHRpcC9nbGl0Y2h0aXAKICAgIHJlc3RhcnQ6ICdubycKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcG9zdGdyZXMKICAgICAgLSByZWRpcwogICAgY29tbWFuZDogJy4vbWFuYWdlLnB5IG1pZ3JhdGUnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUxAcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWdsaXRjaHRpcH0nCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfRU5DUllQVElPTgogICAgICAtICdFTUFJTF9VUkw9JHtFTUFJTF9VUkw6LWNvbnNvbGVtYWlsOi8vfScKICAgICAgLSAnREVGQVVMVF9GUk9NX0VNQUlMPSR7REVGQVVMVF9GUk9NX0VNQUlMOi10ZXN0QGV4YW1wbGUuY29tfScKICAgICAgLSAnQ0VMRVJZX1dPUktFUl9BVVRPU0NBTEU9JHtDRUxFUllfV09SS0VSX0FVVE9TQ0FMRTotMSwzfScKICAgICAgLSAnQ0VMRVJZX1dPUktFUl9NQVhfVEFTS1NfUEVSX0NISUxEPSR7Q0VMRVJZX1dPUktFUl9NQVhfVEFTS1NfUEVSX0NISUxEOi0xMDAwMH0nCg==","tags":["error","tracking","open-source","self-hosted","sentry"],"logo":"svgs\/glitchtip.png","minversion":"0.0.0","port":"8080"},"grafana-with-postgresql":{"documentation":"https:\/\/grafana.com?utm_source=coolify.io","slogan":"Grafana is the open source analytics & monitoring solution for every database.","compose":"c2VydmljZXM6CiAgZ3JhZmFuYToKICAgIGltYWdlOiBncmFmYW5hL2dyYWZhbmEtb3NzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fR1JBRkFOQV8zMDAwCiAgICAgIC0gJ0dGX1NFUlZFUl9ST09UX1VSTD0ke1NFUlZJQ0VfRlFETl9HUkFGQU5BfScKICAgICAgLSAnR0ZfU0VSVkVSX0RPTUFJTj0ke1NFUlZJQ0VfRlFETl9HUkFGQU5BfScKICAgICAgLSAnR0ZfU0VDVVJJVFlfQURNSU5fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX0dSQUZBTkF9JwogICAgICAtIEdGX0RBVEFCQVNFX1RZUEU9cG9zdGdyZXMKICAgICAgLSBHRl9EQVRBQkFTRV9IT1NUPXBvc3RncmVzcWwKICAgICAgLSBHRl9EQVRBQkFTRV9VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBHRl9EQVRBQkFTRV9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdHRl9EQVRBQkFTRV9OQU1FPSR7UE9TVEdSRVNfREI6LWdyYWZhbmF9JwogICAgdm9sdW1lczoKICAgICAgLSAnZ3JhZmFuYS1kYXRhOi92YXIvbGliL2dyYWZhbmEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMC9hcGkvaGVhbHRoJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzcWwKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotZ3JhZmFuYX0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["grafana","analytics","monitoring","dashboard"],"logo":"svgs\/grafana.svg","minversion":"0.0.0","port":"3000"},"grafana":{"documentation":"https:\/\/grafana.com?utm_source=coolify.io","slogan":"Grafana is the open source analytics & monitoring solution for every database.","compose":"c2VydmljZXM6CiAgZ3JhZmFuYToKICAgIGltYWdlOiBncmFmYW5hL2dyYWZhbmEtb3NzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fR1JBRkFOQV8zMDAwCiAgICAgIC0gJ0dGX1NFUlZFUl9ST09UX1VSTD0ke1NFUlZJQ0VfRlFETl9HUkFGQU5BfScKICAgICAgLSAnR0ZfU0VSVkVSX0RPTUFJTj0ke1NFUlZJQ0VfRlFETl9HUkFGQU5BfScKICAgICAgLSAnR0ZfU0VDVVJJVFlfQURNSU5fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX0dSQUZBTkF9JwogICAgdm9sdW1lczoKICAgICAgLSAnZ3JhZmFuYS1kYXRhOi92YXIvbGliL2dyYWZhbmEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMC9hcGkvaGVhbHRoJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["grafana","analytics","monitoring","dashboard"],"logo":"svgs\/grafana.svg","minversion":"0.0.0","port":"3000"},"grocy":{"documentation":"https:\/\/github.com\/grocy\/grocy?utm_source=coolify.io","slogan":"Grocy is a web-based household management and grocery list application.","compose":"c2VydmljZXM6CiAgZ3JvY3k6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvZ3JvY3k6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0dST0NZCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZ3JvY3ktY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["groceries","household","management","grocery","shopping"],"logo":"svgs\/grocy.svg","minversion":"0.0.0"},"heimdall":{"documentation":"https:\/\/github.com\/linuxserver\/Heimdall?utm_source=coolify.io","slogan":"Heimdall is a dashboard for managing and organizing your server applications.","compose":"c2VydmljZXM6CiAgaGVpbWRhbGw6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvaGVpbWRhbGw6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0hFSU1EQUxMCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnaGVpbWRhbGwtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["dashboard","server","applications","interface"],"logo":"svgs\/unknown.svg","minversion":"0.0.0"},"homepage":{"documentation":"https:\/\/gethomepage.dev\/latest\/?utm_source=coolify.io","slogan":"A modern, fully static, fast, secure fully proxied, highly customizable application dashboard","compose":"c2VydmljZXM6CiAgaG9tZXBhZ2U6CiAgICBpbWFnZTogJ2doY3IuaW8vZ2V0aG9tZXBhZ2UvaG9tZXBhZ2U6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0hPTUVQQUdFXzMwMDAKICAgICAgLSAnSE9NRVBBR0VfVkFSX0JBU0U9JHtTRVJWSUNFX0ZRRE5fSE9NRVBBR0V9JwogICAgdm9sdW1lczoKICAgICAgLSAnaG9tZXBhZ2UtY29uZmlnOi9hcHAvY29uZmlnJwogICAgICAtICdob21lcGFnZS1pbWFnZXM6L2FwcC9wdWJsaWMvaW1hZ2VzJwogICAgICAtICcvdmFyL3J1bi9kb2NrZXIuc29jazovdmFyL3J1bi9kb2NrZXIuc29jaycK","tags":["dashboard","homepage"],"logo":"svgs\/homepage.png","minversion":"0.0.0","port":"3000"},"jellyfin":{"documentation":"https:\/\/jellyfin.org?utm_source=coolify.io","slogan":"Jellyfin is a media server for hosting and streaming your media collection.","compose":"c2VydmljZXM6CiAgamVsbHlmaW46CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvamVsbHlmaW46bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0pFTExZRklOXzgwOTYKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICAgIC0gSkVMTFlGSU5fUHVibGlzaGVkU2VydmVyVXJsPSRTRVJWSUNFX0ZRRE5fSkVMTFlGSU4KICAgIHZvbHVtZXM6CiAgICAgIC0gJ2plbGx5ZmluLWNvbmZpZzovY29uZmlnJwogICAgICAtICdqZWxseWZpbi10dnNob3dzOi9kYXRhL3R2c2hvd3MnCiAgICAgIC0gJ2plbGx5ZmluLW1vdmllczovZGF0YS9tb3ZpZXMnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODA5NicKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=","tags":["media","server","movies","tv","music"],"logo":"svgs\/jellyfin.svg","minversion":"0.0.0","port":"8096"},"kuzzle":{"documentation":"https:\/\/kuzzle.io?utm_source=coolify.io","slogan":"Kuzzle is a generic backend offering the basic building blocks common to every application.","compose":"c2VydmljZXM6CiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjctYWxwaW5lJwogICAgY29tbWFuZDogJ3JlZGlzLXNlcnZlciAtLWFwcGVuZG9ubHkgeWVzJwogICAgdm9sdW1lczoKICAgICAgLSAnZWxhc3RpYy1yZWRpcy1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgZWxhc3RpY3NlYXJjaDoKICAgIGltYWdlOiAna3V6emxlaW8vZWxhc3RpY3NlYXJjaDo3JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjkyMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAycwogICAgICByZXRyaWVzOiAxMAogICAgdWxpbWl0czoKICAgICAgbm9maWxlOiA2NTUzNgogIGt1enpsZToKICAgIGltYWdlOiAna3V6emxlaW8va3V6emxlOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9LVVpaTEVfNzUxMgogICAgICAtICdrdXp6bGVfc2VydmljZXNfX3N0b3JhZ2VFbmdpbmVfX2NsaWVudF9fbm9kZT1odHRwOi8vZWxhc3RpY3NlYXJjaDo5MjAwJwogICAgICAtIGt1enpsZV9zZXJ2aWNlc19fc3RvcmFnZUVuZ2luZV9fY29tbW9uTWFwcGluZ19fZHluYW1pYz10cnVlCiAgICAgIC0ga3V6emxlX3NlcnZpY2VzX19pbnRlcm5hbENhY2hlX19ub2RlX19ob3N0PXJlZGlzCiAgICAgIC0ga3V6emxlX3NlcnZpY2VzX19tZW1vcnlTdG9yYWdlX19ub2RlX19ob3N0PXJlZGlzCiAgICAgIC0ga3V6emxlX3NlcnZlcl9fcHJvdG9jb2xzX19tcXR0X19lbmFibGVkPXRydWUKICAgICAgLSBrdXp6bGVfc2VydmVyX19wcm90b2NvbHNfX21xdHRfX2RldmVsb3BtZW50TW9kZT1mYWxzZQogICAgICAtIGt1enpsZV9saW1pdHNfX2xvZ2luc1BlclNlY29uZD01MAogICAgICAtIE5PREVfRU5WPXByb2R1Y3Rpb24KICAgICAgLSAnREVCVUc9JHtERUJVRzota3V6emxlOmNsdXN0ZXI6c3luY30nCiAgICAgIC0gJ0RFQlVHX0RFUFRIPSR7REVCVUdfREVQVEg6LTB9JwogICAgICAtICdERUJVR19NQVhfQVJSQVlfTEVOR1RIPSR7REVCVUdfTUFYX0FSUkFZOi0xMDB9JwogICAgICAtICdERUJVR19FWFBBTkQ9JHtERUJVR19FWFBBTkQ6LW9mZn0nCiAgICAgIC0gJ0RFQlVHX1NIT1dfSElEREVOPXskREVCVUdfU0hPV19ISURERU46LW9ufScKICAgICAgLSAnREVCVUdfQ09MT1JTPSR7REVCVUdfQ09MT1JTOi1vbn0nCiAgICBjYXBfYWRkOgogICAgICAtIFNZU19QVFJBQ0UKICAgIHVsaW1pdHM6CiAgICAgIG5vZmlsZTogNjU1MzYKICAgIHN5c2N0bHM6CiAgICAgIC0gbmV0LmNvcmUuc29tYXhjb25uPTgxOTIKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo3NTEyL19oZWFsdGhjaGVjaycKICAgICAgdGltZW91dDogMXMKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHJldHJpZXM6IDMwCiAgICBkZXBlbmRzX29uOgogICAgICByZWRpczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBlbGFzdGljc2VhcmNoOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5Cg==","tags":["backend","api","realtime","websocket","mqtt","rest","sdk","iot","geofencing","low-code"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"7512"},"listmonk":{"documentation":"https:\/\/listmonk.app\/?utm_source=coolify.io","slogan":"Self-hosted newsletter and mailing list manager","compose":"c2VydmljZXM6CiAgbGlzdG1vbms6CiAgICBpbWFnZTogJ2xpc3Rtb25rL2xpc3Rtb25rOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9MSVNUTU9OS185MDAwCiAgICAgIC0gJ0xJU1RNT05LX2FwcF9fYWRkcmVzcz0wLjAuMC4wOjkwMDAnCiAgICAgIC0gTElTVE1PTktfZGJfX2hvc3Q9cG9zdGdyZXMKICAgICAgLSBMSVNUTU9OS19kYl9fbmFtZT1saXN0bW9uawogICAgICAtIExJU1RNT05LX2RiX191c2VyPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBMSVNUTU9OS19kYl9fcGFzc3dvcmQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSBMSVNUTU9OS19kYl9fcG9ydD01NDMyCiAgICAgIC0gTElTVE1PTktfYXBwX19hZG1pbl91c2VybmFtZT1hZG1pbgogICAgICAtIExJU1RNT05LX2FwcF9fYWRtaW5fcGFzc3dvcmQ9JFNFUlZJQ0VfUEFTU1dPUkRfQURNSU4KICAgICAgLSBUWj1FdGMvVVRDCiAgICB2b2x1bWVzOgogICAgICAtICdsaXN0bW9uay1kYXRhOi9saXN0bW9uay91cGxvYWRzJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLXEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo5MDAwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgbGlzdG1vbmstaW5pdGlhbC1kYXRhYmFzZS1zZXR1cDoKICAgIGltYWdlOiAnbGlzdG1vbmsvbGlzdG1vbms6bGF0ZXN0JwogICAgY29tbWFuZDogJy4vbGlzdG1vbmsgLS1pbnN0YWxsIC0teWVzIC0taWRlbXBvdGVudCcKICAgIHJlc3RhcnQ6ICdubycKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBMSVNUTU9OS19kYl9faG9zdD1wb3N0Z3JlcwogICAgICAtIExJU1RNT05LX2RiX19uYW1lPWxpc3Rtb25rCiAgICAgIC0gTElTVE1PTktfZGJfX3VzZXI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIExJU1RNT05LX2RiX19wYXNzd29yZD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtIExJU1RNT05LX2RiX19wb3J0PTU0MzIKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfREI9bGlzdG1vbmsKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgdm9sdW1lczoKICAgICAgLSAncGctZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["newsletter","mailing list","self-hosted","open source"],"logo":"svgs\/listmonk.svg","minversion":"0.0.0","port":"9000"},"logto":{"documentation":"https:\/\/docs.logto.io\/docs\/tutorials\/get-started\/#logto-oss-self-hosted?utm_source=coolify.io","slogan":"A comprehensive identity solution covering both the front and backend, complete with pre-built infrastructure and enterprise-grade solutions.","compose":"c2VydmljZXM6CiAgbG9ndG86CiAgICBpbWFnZTogJ3N2aGQvbG9ndG86JHtUQUctbGF0ZXN0fScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnRyeXBvaW50OgogICAgICAtIHNoCiAgICAgIC0gJy1jJwogICAgICAtICducG0gcnVuIGNsaSBkYiBzZWVkIC0tIC0tc3dlICYmIG5wbSBzdGFydCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFRSVVNUX1BST1hZX0hFQURFUj0xCiAgICAgIC0gJ0RCX1VSTD1wb3N0Z3JlczovLyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfToke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9QHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU19EQjotbG9ndG99JwogICAgICAtIEVORFBPSU5UPSRMT0dUT19FTkRQT0lOVAogICAgICAtIEFETUlOX0VORFBPSU5UPSRMT0dUT19BRE1JTl9FTkRQT0lOVAogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdleGl0IDAnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6MTQtYWxwaW5lJwogICAgdXNlcjogcG9zdGdyZXMKICAgIGVudmlyb25tZW50OgogICAgICBQT1NUR1JFU19VU0VSOiAnJHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICBQT1NUR1JFU19QQVNTV09SRDogJyR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIFBPU1RHUkVTX0RCOiAnJHtQT1NUR1JFU19EQjotbG9ndG99JwogICAgdm9sdW1lczoKICAgICAgLSAnbG9ndG8tcG9zdGdyZXMtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcGdfaXNyZWFkeQogICAgICAgIC0gJy1VJwogICAgICAgIC0gJFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAgIC0gJy1kJwogICAgICAgIC0gJFBPU1RHUkVTX0RCCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["logto","identity","login","authentication","oauth","oidc","openid"],"logo":"svgs\/logto_dark.svg","minversion":"0.0.0"},"mediawiki":{"documentation":"https:\/\/www.mediawiki.org?utm_source=coolify.io","slogan":"MediaWiki is a collaboration and documentation platform brought to you by a vibrant community.","compose":"c2VydmljZXM6CiAgbWVkaWF3aWtpOgogICAgaW1hZ2U6ICdtZWRpYXdpa2k6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX01FRElBV0lLSV84MAogICAgdm9sdW1lczoKICAgICAgLSAnbWVkaWF3aWtpLWltYWdlczovdmFyL3d3dy9odG1sL2ltYWdlcycKICAgICAgLSAnbWVkaWF3aWtpLXNxbGl0ZTovdmFyL3d3dy9odG1sL2RhdGEnCiAgICAgIC0gJy4vTG9jYWxTZXR0aW5ncy5waHA6L3Zhci93d3cvaHRtbC9Mb2NhbFNldHRpbmdzLnBocCcKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo4MCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["wiki","collaboration","documentation"],"logo":"svgs\/mediawiki.ico","minversion":"0.0.0","port":"80"},"meilisearch":{"documentation":"https:\/\/www.meilisearch.com?utm_source=coolify.io","slogan":"MeiliSearch is a powerful, fast, easy to use and deploy search engine.","compose":"c2VydmljZXM6CiAgbWVpbGlzZWFyY2g6CiAgICBpbWFnZTogJ2dldG1laWxpL21laWxpc2VhcmNoOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NRUlMSVNFQVJDSF83NzAwCiAgICAgIC0gJ01FSUxJX05PX0FOQUxZVElDUz0ke01FSUxJX05PX0FOQUxZVElDUzotdHJ1ZX0nCiAgICAgIC0gJ01FSUxJX0VOVj0ke01FSUxJX0VOVjotcHJvZHVjdGlvbn0nCiAgICAgIC0gJ01FSUxJX01BU1RFUl9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEX01FSUxJU0VBUkNIfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ21laWxpc2VhcmNoLWRhdGE6L21laWxpX2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6NzcwMC9oZWFsdGgnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["search","engine","fulltext","full","text","meilisearch"],"logo":"svgs\/meilisearch.svg","minversion":"0.0.0","port":"7700"},"metabase":{"documentation":"https:\/\/www.metabase.com?utm_source=coolify.io","slogan":"Fast analytics with the friendly UX and integrated tooling to let your company explore data on their own.","compose":"c2VydmljZXM6CiAgbWV0YWJhc2U6CiAgICBpbWFnZTogJ21ldGFiYXNlL21ldGFiYXNlOmxhdGVzdCcKICAgIHZvbHVtZXM6CiAgICAgIC0gJy9kZXYvdXJhbmRvbTovZGV2L3JhbmRvbTpybycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NRVRBQkFTRV8zMDAwCiAgICAgIC0gTUJfREJfVFlQRT1wb3N0Z3JlcwogICAgICAtIE1CX0RCX0hPU1Q9cG9zdGdyZXNxbAogICAgICAtIE1CX0RCX1BPUlQ9NTQzMgogICAgICAtICdNQl9EQl9EQk5BTUU9JHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1tZXRhYmFzZX0nCiAgICAgIC0gTUJfREJfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTUUwKICAgICAgLSBNQl9EQl9QQVNTPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUwKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAnY3VybCAtLWZhaWwgLUkgaHR0cDovLzEyNy4wLjAuMTozMDAwL2FwaS9oZWFsdGggfHwgZXhpdCAxJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAnbWV0YWJhc2UtcG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotbWV0YWJhc2V9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["analytics","bi","business","intelligence"],"logo":"svgs\/metabase.svg","minversion":"0.0.0","port":"3000"},"metube":{"documentation":"https:\/\/github.com\/alexta69\/metube?utm_source=coolify.io","slogan":"A web GUI for youtube-dl with playlist support. It enables you to effortlessly download videos from YouTube and dozens of other sites.","compose":"c2VydmljZXM6CiAgbWV0dWJlOgogICAgaW1hZ2U6ICdnaGNyLmlvL2FsZXh0YTY5L21ldHViZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTUVUVUJFXzgwODEKICAgICAgLSBVSUQ9MTAwMAogICAgICAtIEdJRD0xMDAwCiAgICB2b2x1bWVzOgogICAgICAtICdtZXR1YmUtZG93bmxvYWRzOi9kb3dubG9hZHMnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODA4MScKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=","tags":["youtube","download","videos","playlist"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"8081"},"minio":{"documentation":"https:\/\/min.io\/docs\/minio\/container\/index.html?utm_source=coolify.io","slogan":"MinIO is a high performance object storage server compatible with Amazon S3 APIs.","compose":"c2VydmljZXM6CiAgbWluaW86CiAgICBpbWFnZTogJ3F1YXkuaW8vbWluaW8vbWluaW86bGF0ZXN0JwogICAgY29tbWFuZDogJ3NlcnZlciAvZGF0YSAtLWNvbnNvbGUtYWRkcmVzcyAiOjkwMDEiJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTUlOSU9fU0VSVkVSX1VSTD0kTUlOSU9fU0VSVkVSX1VSTAogICAgICAtIE1JTklPX0JST1dTRVJfUkVESVJFQ1RfVVJMPSRNSU5JT19CUk9XU0VSX1JFRElSRUNUX1VSTAogICAgICAtIE1JTklPX1JPT1RfVVNFUj0kU0VSVklDRV9VU0VSX01JTklPCiAgICAgIC0gTUlOSU9fUk9PVF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9NSU5JTwogICAgdm9sdW1lczoKICAgICAgLSAnbWluaW8tZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo5MDAwL21pbmlvL2hlYWx0aC9saXZlJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["object","storage","server","s3","api"],"logo":"svgs\/minio.svg","minversion":"0.0.0"},"moodle":{"documentation":"https:\/\/moodle.org?utm_source=coolify.io","slogan":"Moodle is the world\u2019s most customisable and trusted eLearning solution that empowers educators to improve our world.","compose":"c2VydmljZXM6CiAgbWFyaWFkYjoKICAgIGltYWdlOiAnbWFyaWFkYjoxMS4xJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gQUxMT1dfRU1QVFlfUEFTU1dPUkQ9bm8KICAgICAgLSBNWVNRTF9ST09UX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1JPT1QKICAgICAgLSBNWVNRTF9EQVRBQkFTRT1iaXRuYW1pX21vb2RsZQogICAgICAtIE1ZU1FMX1VTRVI9JFNFUlZJQ0VfVVNFUl9NQVJJQURCCiAgICAgIC0gTVlTUUxfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfTUFSSUFEQgogICAgICAtIE1BUklBREJfQ0hBUkFDVEVSX1NFVD11dGY4bWI0CiAgICAgIC0gTUFSSUFEQl9DT0xMQVRFPXV0ZjhtYjRfdW5pY29kZV9jaQogICAgdm9sdW1lczoKICAgICAgLSAnbWFyaWFkYi1kYXRhOi92YXIvbGliL215c3FsJwogIG1vb2RsZToKICAgIGltYWdlOiAnZG9ja2VyLmlvL2JpdG5hbWkvbW9vZGxlOjQuMycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NT09ETEVfODA4MAogICAgICAtIE1PT0RMRV9EQVRBQkFTRV9IT1NUPW1hcmlhZGIKICAgICAgLSBNT09ETEVfREFUQUJBU0VfUE9SVF9OVU1CRVI9MzMwNgogICAgICAtIE1PT0RMRV9EQVRBQkFTRV9VU0VSPSRTRVJWSUNFX1VTRVJfTUFSSUFEQgogICAgICAtIE1PT0RMRV9EQVRBQkFTRV9OQU1FPWJpdG5hbWlfbW9vZGxlCiAgICAgIC0gTU9PRExFX0RBVEFCQVNFX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX01BUklBREIKICAgICAgLSBBTExPV19FTVBUWV9QQVNTV09SRD1ubwogICAgICAtICdNT09ETEVfVVNFUk5BTUU9JHtNT09ETEVfVVNFUk5BTUU6LXVzZXJ9JwogICAgICAtIE1PT0RMRV9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9NT09ETEUKICAgICAgLSBNT09ETEVfRU1BSUw9dXNlckBleGFtcGxlLmNvbQogICAgICAtICdNT09ETEVfU0lURV9OQU1FPSR7TU9PRExFX1NJVEVfTkFNRTotTmV3IFNpdGV9JwogICAgdm9sdW1lczoKICAgICAgLSAnbW9vZGxlLWRhdGE6L2JpdG5hbWkvbW9vZGxlJwogICAgICAtICdtb29kbGVkYXRhLWRhdGE6L2JpdG5hbWkvbW9vZGxlZGF0YScKICAgIGRlcGVuZHNfb246CiAgICAgIC0gbWFyaWFkYgo=","tags":["moodle","elearning","education","lms","cms","open","source","low","code"],"logo":"svgs\/moodle.png","minversion":"0.0.0","port":"8080"},"n8n-with-postgresql":{"documentation":"https:\/\/n8n.io?utm_source=coolify.io","slogan":"n8n is an extendable workflow automation tool.","compose":"c2VydmljZXM6CiAgbjhuOgogICAgaW1hZ2U6IGRvY2tlci5uOG4uaW8vbjhuaW8vbjhuCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTjhOXzU2NzgKICAgICAgLSAnTjhOX0VESVRPUl9CQVNFX1VSTD0ke1NFUlZJQ0VfRlFETl9OOE59JwogICAgICAtICdXRUJIT09LX1VSTD0ke1NFUlZJQ0VfRlFETl9OOE59JwogICAgICAtICdOOE5fSE9TVD0ke1NFUlZJQ0VfVVJMX044Tn0nCiAgICAgIC0gR0VORVJJQ19USU1FWk9ORT1FdXJvcGUvQmVybGluCiAgICAgIC0gVFo9RXVyb3BlL0JlcmxpbgogICAgICAtIERCX1RZUEU9cG9zdGdyZXNkYgogICAgICAtICdEQl9QT1NUR1JFU0RCX0RBVEFCQVNFPSR7UE9TVEdSRVNfREI6LW44bn0nCiAgICAgIC0gREJfUE9TVEdSRVNEQl9IT1NUPXBvc3RncmVzcWwKICAgICAgLSBEQl9QT1NUR1JFU0RCX1BPUlQ9NTQzMgogICAgICAtIERCX1BPU1RHUkVTREJfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gREJfUE9TVEdSRVNEQl9TQ0hFTUE9cHVibGljCiAgICAgIC0gREJfUE9TVEdSRVNEQl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgdm9sdW1lczoKICAgICAgLSAnbjhuLWRhdGE6L2hvbWUvbm9kZS8ubjhuJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICd3Z2V0IC1xTy0gaHR0cDovLzEyNy4wLjAuMTo1Njc4LycKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1uOG59JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["n8n","workflow","automation","open","source","low","code"],"logo":"svgs\/n8n.png","minversion":"0.0.0","port":"5678"},"n8n":{"documentation":"https:\/\/n8n.io?utm_source=coolify.io","slogan":"n8n is an extendable workflow automation tool.","compose":"c2VydmljZXM6CiAgbjhuOgogICAgaW1hZ2U6IGRvY2tlci5uOG4uaW8vbjhuaW8vbjhuCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTjhOXzU2NzgKICAgICAgLSAnTjhOX0VESVRPUl9CQVNFX1VSTD0ke1NFUlZJQ0VfRlFETl9OOE59JwogICAgICAtICdXRUJIT09LX1VSTD0ke1NFUlZJQ0VfRlFETl9OOE59JwogICAgICAtICdOOE5fSE9TVD0ke1NFUlZJQ0VfVVJMX044Tn0nCiAgICAgIC0gR0VORVJJQ19USU1FWk9ORT1FdXJvcGUvQmVybGluCiAgICAgIC0gVFo9RXVyb3BlL0JlcmxpbgogICAgdm9sdW1lczoKICAgICAgLSAnbjhuLWRhdGE6L2hvbWUvbm9kZS8ubjhuJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICd3Z2V0IC1xTy0gaHR0cDovLzEyNy4wLjAuMTo1Njc4LycKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["n8n","workflow","automation","open","source","low","code"],"logo":"svgs\/n8n.png","minversion":"0.0.0","port":"5678"},"next-image-transformation":{"documentation":"https:\/\/github.com\/coollabsio\/next-image-transformation?utm_source=coolify.io","slogan":"Drop-in replacement for Vercel's Nextjs image optimization service.","compose":"c2VydmljZXM6CiAgbmV4dC1pbWFnZS10cmFuc2Zvcm1hdGlvbjoKICAgIGltYWdlOiAnZ2hjci5pby9jb29sbGFic2lvL25leHQtaW1hZ2UtdHJhbnNmb3JtYXRpb246bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1RSQU5TRk9STUFUSU9OXzMwMDAKICAgICAgLSBOT0RFX0VOVj1wcm9kdWN0aW9uCiAgICAgIC0gJ0FMTE9XRURfUkVNT1RFX0RPTUFJTlM9JHtBTExPV0VEX1JFTU9URV9ET01BSU5TOi0qfScKICAgICAgLSAnSU1HUFJPWFlfVVJMPSR7SU1HUFJPWFlfVVJMOi1odHRwOi8vaW1ncHJveHk6ODA4MH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogJ3dnZXQgLXFPLSBodHRwOi8vMTI3LjAuMC4xOjMwMDAvaGVhbHRoIHx8IGV4aXQgMScKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiA1CiAgaW1ncHJveHk6CiAgICBpbWFnZTogZGFydGhzaW0vaW1ncHJveHkKICAgIGVudmlyb25tZW50OgogICAgICAtIElNR1BST1hZX0VOQUJMRV9XRUJQX0RFVEVDVElPTj10cnVlCiAgICAgIC0gSU1HUFJPWFlfSlBFR19QUk9HUkVTU0lWRT10cnVlCiAgICAgIC0gSU1HUFJPWFlfVVNFX0VUQUc9dHJ1ZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGltZ3Byb3h5CiAgICAgICAgLSBoZWFsdGgKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiA1Cg==","tags":["nextjs","image","transformation","service"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"3000"},"nextcloud":{"documentation":"https:\/\/docs.nextcloud.com?utm_source=coolify.io","slogan":"NextCloud is a self-hosted, open-source platform that provides file storage, collaboration, and communication tools for seamless data management.","compose":"c2VydmljZXM6CiAgbmV4dGNsb3VkOgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL25leHRjbG91ZDpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTkVYVENMT1VECiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnbmV4dGNsb3VkLWNvbmZpZzovY29uZmlnJwogICAgICAtICduZXh0Y2xvdWQtZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=","tags":["cloud","collaboration","communication","filestorage","data"],"logo":"svgs\/nextcloud.svg","minversion":"0.0.0"},"nocodb":{"documentation":"https:\/\/nocodb.com\/?utm_source=coolify.io","slogan":"NocoDB is an open source Airtable alternative. Turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart-spreadsheet.","compose":"c2VydmljZXM6CiAgbm9jb2RiOgogICAgaW1hZ2U6IG5vY29kYi9ub2NvZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9OT0NPREJfODA4MAogICAgdm9sdW1lczoKICAgICAgLSAnbm9jb2RiLWRhdGE6L3Vzci9hcHAvZGF0YS8nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gd2dldAogICAgICAgIC0gJy1xJwogICAgICAgIC0gJy0tc3BpZGVyJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODA4MCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["nocodb","airtable","mysql","postgresql","sqlserver","sqlite","mariadb"],"logo":"svgs\/nocodb.svg","minversion":"0.0.0","port":"8080"},"odoo":{"documentation":"https:\/\/www.odoo.com\/?utm_source=coolify.io","slogan":"Odoo is a suite of open-source business apps that cover all your company needs.","compose":"c2VydmljZXM6CiAgb2RvbzoKICAgIGltYWdlOiAnb2RvbzoxNycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9PRE9PXzgwNjkKICAgICAgLSBIT1NUPXBvc3RncmVzcWwKICAgICAgLSBVU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBQQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgdm9sdW1lczoKICAgICAgLSAnb2Rvby13ZWItZGF0YTovdmFyL2xpYi9vZG9vJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwNjknCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMzAKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19EQj1wb3N0Z3JlcwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kIHBvc3RncmVzJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["business","apps","crm","ecommerce","accounting","inventory","point of sale","project management","open-source"],"logo":"svgs\/odoo.svg","minversion":"0.0.0","port":"8069"},"openblocks":{"documentation":"https:\/\/openblocks.dev?utm_source=coolify.io","slogan":"OpenBlocks is a self-hosted, open-source, low-code platform for building internal tools.","compose":"c2VydmljZXM6CiAgb3BlbmJsb2NrczoKICAgIGltYWdlOiBvcGVuYmxvY2tzZGV2L29wZW5ibG9ja3MtY2UKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9PUEVOQkxPQ0tTXzMwMDAKICAgICAgLSAnRU5BQkxFX1VTRVJfU0lHTl9VUD0ke0VOQUJMRV9VU0VSX1NJR05fVVA6LXRydWV9JwogICAgICAtIEVOQ1JZUFRJT05fUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfRU5DUllQVElPTgogICAgICAtIEVOQ1JZUFRJT05fU0FMVD0kU0VSVklDRV9QQVNTV09SRF9TQUxUCiAgICB2b2x1bWVzOgogICAgICAtICdvcGVuYmxvY2tzLWRhdGE6L29wZW5ibG9ja3Mtc3RhY2tzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjMwMDAvaGVhbHRoJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["openblocks","low","code","platform","open","source","low","code"],"logo":"svgs\/openblocks.svg","minversion":"0.0.0","port":"3000"},"pairdrop":{"documentation":"https:\/\/pairdrop.net\/?utm_source=coolify.io","slogan":"Pairdrop is a self-hosted file sharing and collaboration platform, offering secure file sharing and collaboration capabilities for efficient teamwork.","compose":"c2VydmljZXM6CiAgcGFpcmRyb3A6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvcGFpcmRyb3A6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1BBSVJEUk9QXzMwMDAKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICAgIC0gREVCVUdfTU9ERT1mYWxzZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjMwMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["file","sharing","collaboration","teamwork"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"3000"},"penpot":{"documentation":"https:\/\/help.penpot.app\/technical-guide\/getting-started\/#install-with-docker?utm_source=coolify.io","slogan":"Penpot is the first Open Source design and prototyping platform for product teams.","compose":"c2VydmljZXM6CiAgZnJvbnRlbmQ6CiAgICBpbWFnZTogJ3BlbnBvdGFwcC9mcm9udGVuZDpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdwZW5wb3QtYXNzZXRzOi9vcHQvZGF0YS9hc3NldHMnCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBlbnBvdC1iYWNrZW5kCiAgICAgIC0gcGVucG90LWV4cG9ydGVyCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fRlJPTlRFTkQKICAgICAgLSAnUEVOUE9UX0ZMQUdTPSR7UEVOUE9UX0ZST05URU5EX0ZMQUdTOi1lbmFibGUtbG9naW4td2l0aC1wYXNzd29yZH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICBwZW5wb3QtYmFja2VuZDoKICAgIGltYWdlOiAncGVucG90YXBwL2JhY2tlbmQ6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAncGVucG90LWFzc2V0czovb3B0L2RhdGEvYXNzZXRzJwogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3JlcwogICAgICAtIHJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUEVOUE9UX0ZMQUdTPSR7UEVOUE9UX0JBQ0tFTkRfRkxBR1M6LWVuYWJsZS1sb2dpbi13aXRoLXBhc3N3b3JkIGVuYWJsZS1zbXRwIGVuYWJsZS1wcmVwbC1zZXJ2ZXJ9JwogICAgICAtIFBFTlBPVF9IVFRQX1NFUlZFUl9QT1JUPTYwNjAKICAgICAgLSBQRU5QT1RfU0VDUkVUX0tFWT0kU0VSVklDRV9SRUFMQkFTRTY0XzY0X1BFTlBPVAogICAgICAtIFBFTlBPVF9QVUJMSUNfVVJJPSRTRVJWSUNFX0ZRRE5fRlJPTlRFTkQKICAgICAgLSAnUEVOUE9UX0JBQ0tFTkRfVVJJPWh0dHA6Ly9wZW5wb3QtYmFja2VuZCcKICAgICAgLSAnUEVOUE9UX0VYUE9SVEVSX1VSST1odHRwOi8vcGVucG90LWV4cG9ydGVyJwogICAgICAtICdQRU5QT1RfREFUQUJBU0VfVVJJPXBvc3RncmVzcWw6Ly9wb3N0Z3Jlcy8ke1BPU1RHUkVTX0RCOi1wZW5wb3R9JwogICAgICAtICdQRU5QT1RfREFUQUJBU0VfVVNFUk5BTUU9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQRU5QT1RfREFUQUJBU0VfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfScKICAgICAgLSAnUEVOUE9UX1JFRElTX1VSST1yZWRpczovL3JlZGlzLzAnCiAgICAgIC0gUEVOUE9UX0FTU0VUU19TVE9SQUdFX0JBQ0tFTkQ9YXNzZXRzLWZzCiAgICAgIC0gUEVOUE9UX1NUT1JBR0VfQVNTRVRTX0ZTX0RJUkVDVE9SWT0vb3B0L2RhdGEvYXNzZXRzCiAgICAgIC0gJ1BFTlBPVF9URUxFTUVUUllfRU5BQkxFRD0ke1BFTlBPVF9URUxFTUVUUllfRU5BQkxFRDotZmFsc2V9JwogICAgICAtICdQRU5QT1RfU01UUF9ERUZBVUxUX0ZST009JHtQRU5QT1RfU01UUF9ERUZBVUxUX0ZST006LW5vLXJlcGx5QGV4YW1wbGUuY29tfScKICAgICAgLSAnUEVOUE9UX1NNVFBfREVGQVVMVF9SRVBMWV9UTz0ke1BFTlBPVF9TTVRQX0RFRkFVTFRfUkVQTFlfVE86LW5vLXJlcGx5QGV4YW1wbGUuY29tfScKICAgICAgLSAnUEVOUE9UX1NNVFBfSE9TVD0ke1BFTlBPVF9TTVRQX0hPU1Q6LW1haWxwaXR9JwogICAgICAtICdQRU5QT1RfU01UUF9QT1JUPSR7UEVOUE9UX1NNVFBfUE9SVDotMTAyNX0nCiAgICAgIC0gJ1BFTlBPVF9TTVRQX1VTRVJOQU1FPSR7UEVOUE9UX1NNVFBfVVNFUk5BTUU6LXBlbnBvdH0nCiAgICAgIC0gJ1BFTlBPVF9TTVRQX1BBU1NXT1JEPSR7UEVOUE9UX1NNVFBfUEFTU1dPUkQ6LXBlbnBvdH0nCiAgICAgIC0gJ1BFTlBPVF9TTVRQX1RMUz0ke1BFTlBPVF9TTVRQX1RMUzotZmFsc2V9JwogICAgICAtICdQRU5QT1RfU01UUF9TU0w9JHtQRU5QT1RfU01UUF9TU0w6LWZhbHNlfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo2MDYwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgcGVucG90LWV4cG9ydGVyOgogICAgaW1hZ2U6ICdwZW5wb3RhcHAvZXhwb3J0ZXI6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUEVOUE9UX1BVQkxJQ19VUkk9JFNFUlZJQ0VfRlFETl9GUk9OVEVORAogICAgICAtICdQRU5QT1RfUkVESVNfVVJJPXJlZGlzOi8vcmVkaXMvMCcKICBtYWlscGl0OgogICAgaW1hZ2U6ICdheGxsZW50L21haWxwaXQ6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX01BSUxQSVRfODAyNQogIHBvc3RncmVzOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BlbnBvdC1wb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfSU5JVERCX0FSR1M9LS1kYXRhLWNoZWNrc3VtcwogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LXBlbnBvdH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjctYWxwaW5lJwogICAgY29tbWFuZDogJ3JlZGlzLXNlcnZlciAtLWFwcGVuZG9ubHkgeWVzJwogICAgdm9sdW1lczoKICAgICAgLSAncGVucG90LXJlZGlzLWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["penpot","design","prototyping","figma","open","source"],"logo":"svgs\/unknown.svg","minversion":"0.0.0"},"phpmyadmin":{"documentation":"https:\/\/phpmyadmin.net?utm_source=coolify.io","slogan":"phpMyAdmin is a web-based database management tool for administering your MySQL and MariaDB databases through a user-friendly interface.","compose":"c2VydmljZXM6CiAgcGhwbXlhZG1pbjoKICAgIGltYWdlOiAnbHNjci5pby9saW51eHNlcnZlci9waHBteWFkbWluOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9QSFBNWUFETUlOCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgICAtIFBNQV9BUkJJVFJBUlk9MQogICAgICAtIFBNQV9BQlNPTFVURV9VUkk9JFNFUlZJQ0VfRlFETl9QSFBNWUFETUlOCiAgICB2b2x1bWVzOgogICAgICAtICdwaHBteWFkbWluLWNvbmZpZzovY29uZmlnJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==","tags":["database management"],"logo":"svgs\/phpmyadmin.svg","minversion":"0.0.0"},"pocketbase":{"documentation":"https:\/\/pocketbase.io\/docs\/?utm_source=coolify.io","slogan":"Open Source backend for your next SaaS and Mobile app in 1 file","compose":"c2VydmljZXM6CiAgcG9ja2V0YmFzZToKICAgIGltYWdlOiAnZ2hjci5pby9jb29sbGFic2lvL3BvY2tldGJhc2U6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1BPQ0tFVEJBU0VfODA4MAogICAgdm9sdW1lczoKICAgICAgLSAncG9ja2V0YmFzZS1kYXRhOi9hcHAvcGJfZGF0YScKICAgICAgLSAncG9ja2V0YmFzZS1ob29rczovYXBwL3BiX2hvb2tzJwo=","tags":["pocketbase","backend","saas","mobile","api"],"logo":"svgs\/pocketbase.svg","minversion":"0.0.0","port":"8080"},"posthog":{"documentation":"https:\/\/posthog.com?utm_source=coolify.io","slogan":"The single platform to analyze, test, observe, and deploy new features","compose":"c2VydmljZXM6CiAgZGI6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjEyLWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3Rob2ctcG9zdGdyZXMtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19VU0VSPXBvc3Rob2cKICAgICAgLSBQT1NUR1JFU19EQj1wb3N0aG9nCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSBwb3N0aG9nJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjYuMi43LWFscGluZScKICAgIGNvbW1hbmQ6ICdyZWRpcy1zZXJ2ZXIgLS1tYXhtZW1vcnktcG9saWN5IGFsbGtleXMtbHJ1IC0tbWF4bWVtb3J5IDIwMG1iJwogIGNsaWNraG91c2U6CiAgICBpbWFnZTogJ2NsaWNraG91c2UvY2xpY2tob3VzZS1zZXJ2ZXI6MjMuMTEuMi4xMS1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9pZGwvZXZlbnRzX2RlYWRfbGV0dGVyX3F1ZXVlLmpzb24KICAgICAgICB0YXJnZXQ6IC9pZGwvZXZlbnRzX2RlYWRfbGV0dGVyX3F1ZXVlLmpzb24KICAgICAgICBjb250ZW50OiAie1xuICBcIiRzY2hlbWFcIjogXCJodHRwczovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC8yMDIwLTEyL3NjaGVtYVwiLFxuICBcIiRpZFwiOiBcImZpbGU6Ly9wb3N0aG9nL2lkbC9ldmVudHNfZGVhZF9sZXR0ZXJfcXVldWUuanNvblwiLFxuICBcInRpdGxlXCI6IFwiZXZlbnRzX2RlYWRfbGV0dGVyX3F1ZXVlXCIsXG4gIFwiZGVzY3JpcHRpb25cIjogXCJFdmVudHMgdGhhdCBmYWlsZWQgdG8gYmUgdmFsaWRhdGVkIG9yIHByb2Nlc3NlZCBhbmQgYXJlIHNlbnQgdG8gdGhlIERMUVwiLFxuICBcInR5cGVcIjogXCJvYmplY3RcIixcbiAgXCJwcm9wZXJ0aWVzXCI6IHtcbiAgICAgIFwiaWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJ1dWlkIGZvciB0aGUgc3VibWlzc2lvblwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICB9LFxuICAgICAgXCJldmVudF91dWlkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwidXVpZCBmb3IgdGhlIGV2ZW50XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImV2ZW50XCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiZXZlbnQgdHlwZVwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICB9LFxuICAgICAgXCJwcm9wZXJ0aWVzXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBwcm9wZXJ0aWVzIGpzb24gb2JqZWN0XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImRpc3RpbmN0X2lkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiUG9zdEhvZyBkaXN0aW5jdF9pZFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICB9LFxuICAgICAgXCJ0ZWFtX2lkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwidGVhbV9pZCAobWFwcyB0byB0aGUgcHJvamVjdCB1bmRlciB0aGUgb3JnYW5pemF0aW9uKVwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJlbGVtZW50c19jaGFpblwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlVzZWQgZm9yIGF1dG9jYXB0dXJlLiBET00gZWxlbWVudCBoaWVyYXJjaHlcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgfSxcbiAgICAgIFwiY3JlYXRlZF9hdFwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlVzZWQgZm9yIGF1dG9jYXB0dXJlLiBET00gZWxlbWVudCBoaWVyYXJjaHlcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgfSxcbiAgICAgIFwiaXBcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJJUCBBZGRyZXNzIG9mIHRoZSBhc3NvY2lhdGVkIHdpdGggdGhlIGV2ZW50XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcInNpdGVfdXJsXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU2l0ZSBVUkwgYXNzb2NpYXRlZCB3aXRoIHRoZSBldmVudCB0aGUgZXZlbnRcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgfSxcbiAgICAgIFwibm93XCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiVGltZXN0YW1wIG9mIHRoZSBETFEgZXZlbnRcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgfSxcbiAgICAgIFwicmF3X3BheWxvYWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJSYXcgcGF5bG9hZCBvZiB0aGUgZXZlbnQgdGhhdCBmYWlsZWQgdG8gYmUgY29uc3VtZWRcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgfSxcbiAgICAgIFwiZXJyb3JfdGltZXN0YW1wXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiVGltZXN0YW1wIHRoYXQgdGhlIGVycm9yIG9mIGluZ2VzdGlvbiBvY2N1cnJlZFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJlcnJvcl9sb2NhdGlvblwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlNvdXJjZSBvZiBlcnJvciBpZiBrbm93blwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICB9LFxuICAgICAgXCJlcnJvclwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIkVycm9yIGlmIGtub3duXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcInRhZ3NcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJUYWdzIGFzc29jaWF0ZWQgd2l0aCB0aGUgZXJyb3Igb3IgZXZlbnRcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJhcnJheVwiLFxuICAgICAgICAgIFwiaXRlbXNcIjoge1xuICAgICAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgICAgIH1cbiAgICAgIH1cbiAgfSxcbiAgXCJyZXF1aXJlZFwiOiBbXCJyYXdfcGF5bG9hZFwiXVxufVxuIgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9pZGwvZXZlbnRzX2pzb24uanNvbgogICAgICAgIHRhcmdldDogL2lkbC9ldmVudHNfanNvbi5qc29uCiAgICAgICAgY29udGVudDogIntcbiAgXCIkc2NoZW1hXCI6IFwiaHR0cHM6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQvMjAyMC0xMi9zY2hlbWFcIixcbiAgXCIkaWRcIjogXCJmaWxlOi8vcG9zdGhvZy9pZGwvZXZlbnRzX2pzb24uanNvblwiLFxuICBcInRpdGxlXCI6IFwiZXZlbnRzX2pzb25cIixcbiAgXCJkZXNjcmlwdGlvblwiOiBcIkV2ZW50IHNjaGVtYSB0aGF0IGlzIGRlc3RpbmVkIGZvciBDbGlja0hvdXNlXCIsXG4gIFwidHlwZVwiOiBcIm9iamVjdFwiLFxuICBcInByb3BlcnRpZXNcIjoge1xuICAgICAgXCJ1dWlkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwidXVpZCBmb3IgdGhlIGV2ZW50XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImV2ZW50XCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiZXZlbnQgdHlwZVwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICB9LFxuICAgICAgXCJwcm9wZXJ0aWVzXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBwcm9wZXJ0aWVzIGpzb24gb2JqZWN0XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcInRpbWVzdGFtcFwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlRpbWVzdGFtcCB0aGF0IHRoZSBldmVudCBvY2N1cnJlZFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJ0ZWFtX2lkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwidGVhbV9pZCAobWFwcyB0byB0aGUgcHJvamVjdCB1bmRlciB0aGUgb3JnYW5pemF0aW9uKVwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJkaXN0aW5jdF9pZFwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlBvc3RIb2cgZGlzdGluY3RfaWRcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgfSxcbiAgICAgIFwiZWxlbWVudHNfY2hhaW5cIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJVc2VkIGZvciBhdXRvY2FwdHVyZS4gRE9NIGVsZW1lbnQgaGllcmFyY2h5XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImNyZWF0ZWRfYXRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJUaW1lc3RhbXAgd2hlbiBldmVudCB3YXMgY3JlYXRlZFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJwZXJzb25faWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJVVUlEIGZvciB0aGUgYXNzb2NpYXRlZCBwZXJzb24gaWYgYXZhaWxhYmxlXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcInBlcnNvbl9jcmVhdGVkX2F0XCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiVGltZXN0YW1wIGZvciB3aGVuIHRoZSBhc3NvY2lhdGVkIHBlcnNvbiB3YXMgY3JlYXRlZFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJwZXJzb25fcHJvcGVydGllc1wiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgcGVyc29uIEpTT04gb2JqZWN0XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImdyb3VwMF9wcm9wZXJ0aWVzXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIGEgZ3JvdXAncyBwcm9wZXJ0aWVzXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImdyb3VwMV9wcm9wZXJ0aWVzXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIGEgZ3JvdXAncyBwcm9wZXJ0aWVzXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImdyb3VwMl9wcm9wZXJ0aWVzXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIGEgZ3JvdXAncyBwcm9wZXJ0aWVzXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImdyb3VwM19wcm9wZXJ0aWVzXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIGEgZ3JvdXAncyBwcm9wZXJ0aWVzXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImdyb3VwNF9wcm9wZXJ0aWVzXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIGEgZ3JvdXAncyBwcm9wZXJ0aWVzXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImdyb3VwMF9jcmVhdGVkX2F0XCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiR3JvdXAncyBjcmVhdGlvbiB0aW1lc3RhbXBcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgfSxcbiAgICAgIFwiZ3JvdXAxX2NyZWF0ZWRfYXRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJHcm91cCdzIGNyZWF0aW9uIHRpbWVzdGFtcFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJncm91cDJfY3JlYXRlZF9hdFwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIkdyb3VwJ3MgY3JlYXRpb24gdGltZXN0YW1wXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwibnVtYmVyXCJcbiAgICAgIH0sXG4gICAgICBcImdyb3VwM19jcmVhdGVkX2F0XCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiR3JvdXAncyBjcmVhdGlvbiB0aW1lc3RhbXBcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgfSxcbiAgICAgIFwiZ3JvdXA0X2NyZWF0ZWRfYXRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJHcm91cCdzIGNyZWF0aW9uIHRpbWVzdGFtcFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9XG4gIH0sXG4gIFwicmVxdWlyZWRcIjogW1widXVpZFwiLCBcImV2ZW50XCIsIFwicHJvcGVydGllc1wiLCBcInRpbWVzdGFtcFwiLCBcInRlYW1faWRcIl1cbn1cbiIKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vaWRsL2dyb3Vwcy5qc29uCiAgICAgICAgdGFyZ2V0OiAvaWRsL2dyb3Vwcy5qc29uCiAgICAgICAgY29udGVudDogIntcbiAgXCIkc2NoZW1hXCI6IFwiaHR0cHM6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQvMjAyMC0xMi9zY2hlbWFcIixcbiAgXCIkaWRcIjogXCJmaWxlOi8vcG9zdGhvZy9pZGwvZ3JvdXBzLmpzb25cIixcbiAgXCJ0aXRsZVwiOiBcImdyb3Vwc1wiLFxuICBcImRlc2NyaXB0aW9uXCI6IFwiR3JvdXBzIHNjaGVtYSB0aGF0IGlzIGRlc3RpbmVkIGZvciBDbGlja0hvdXNlXCIsXG4gIFwidHlwZVwiOiBcIm9iamVjdFwiLFxuICBcInByb3BlcnRpZXNcIjoge1xuICAgICAgXCJncm91cF90eXBlX2luZGV4XCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiR3JvdXAgdHlwZSBpbmRleFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJncm91cF9rZXlcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJHcm91cCBLZXlcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgfSxcbiAgICAgIFwiY3JlYXRlZF9hdFwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIkdyb3VwIGNyZWF0aW9uIHRpbWVzdGFtcFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJ0ZWFtX2lkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiVGVhbSBJRCBhc3NvY2lhdGVkIHdpdGggZ3JvdXBcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgfSxcbiAgICAgIFwiZ3JvdXBfcHJvcGVydGllc1wiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlN0cmluZyByZXByZXNlbnRhdGlvbiBvZiBncm91cCBKU09OIHByb3BlcnRpZXMgb2JqZWN0XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH1cbiAgfSxcbiAgXCJyZXF1aXJlZFwiOiBbXCJncm91cF90eXBlX2luZGV4XCIsIFwiZ3JvdXBfa2V5XCIsIFwiY3JlYXRlZF9hdFwiLCBcInRlYW1faWRcIiwgXCJncm91cF9wcm9wZXJ0aWVzXCJdXG59XG4iCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2lkbC9pZGwubWQKICAgICAgICB0YXJnZXQ6IC9pZGwvaWRsLm1kCiAgICAgICAgY29udGVudDogIiMgSURMIC0gSW50ZXJmYWNlIERlZmluaXRpb24gTGFuZ3VhZ2VcblxuVGhpcyBkaXJlY3RvcnkgaXMgcmVzcG9uc2libGUgZm9yIGRlZmluaW5nIHRoZSBzY2hlbWFzIG9mIHRoZSBkYXRhIGJldHdlZW4gc2VydmljZXMuXG5QcmltYXJpbHkgdGhpcyB3aWxsIGJlIGJldHdlZW4gc2VydmljZXMgYW5kIENsaWNrSG91c2UsIGJ1dCBjYW4gYmUgcmVhbGx5IGFueSB0aGluZyBhdCB0aGUgYm91bmRyeSBvZiBzZXJ2aWNlcy5cblxuVGhlIHJlYXNvbiB3aHkgd2UgZG8gdGhpcyBpcyBiZWNhdXNlIGl0IG1ha2VzIGdlbmVyYXRpbmcgY29kZSwgdmFsaWRhdGluZyBkYXRhLCBhbmQgdW5kZXJzdGFuZGluZyB0aGUgc3lzdGVtIGEgd2hvbGUgbG90IGVhc2llci4gV2UndmUgaGFkIGEgZmV3IGN1c3RvbWVycyByZXF1ZXN0IHRoaXMgb2YgdXMgZm9yIGVuZ2luZWVyaW5nIGEgZGVlcGVyIGludGVncmF0aW9uIHdpdGggdXMuXG4iCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2lkbC9wZXJzb24uanNvbgogICAgICAgIHRhcmdldDogL2lkbC9wZXJzb24uanNvbgogICAgICAgIGNvbnRlbnQ6ICJ7XG4gIFwiJHNjaGVtYVwiOiBcImh0dHBzOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LzIwMjAtMTIvc2NoZW1hXCIsXG4gIFwiJGlkXCI6IFwiZmlsZTovL3Bvc3Rob2cvaWRsL3BlcnNvbi5qc29uXCIsXG4gIFwidGl0bGVcIjogXCJwZXJzb25cIixcbiAgXCJkZXNjcmlwdGlvblwiOiBcIlBlcnNvbiBzY2hlbWEgdGhhdCBpcyBkZXN0aW5lZCBmb3IgQ2xpY2tIb3VzZVwiLFxuICBcInR5cGVcIjogXCJvYmplY3RcIixcbiAgXCJwcm9wZXJ0aWVzXCI6IHtcbiAgICAgIFwiaWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJVVUlEIGZvciB0aGUgcGVyc29uXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImNyZWF0ZWRfYXRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJQZXJzb24gY3JlYXRpb24gdGltZXN0YW1wXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwibnVtYmVyXCJcbiAgICAgIH0sXG4gICAgICBcInRlYW1faWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJUZWFtIElEIGFzc29jaWF0ZWQgd2l0aCBwZXJzb25cIixcbiAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgfSxcbiAgICAgIFwicHJvcGVydGllc1wiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlN0cmluZyByZXByZXNlbnRhdGlvbiBvZiBwZXJzb24gSlNPTiBwcm9wZXJ0aWVzIG9iamVjdFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICB9LFxuICAgICAgXCJpc19pZGVudGlmaWVkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiQm9vbGVhbiBpcyB0aGUgcGVyc29uIGlkZW50aWZpZWQ\/XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwiYm9vbGVhblwiXG4gICAgICB9LFxuICAgICAgXCJpc19kZWxldGVkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiQm9vbGVhbiBpcyB0aGUgcGVyc29uIGRlbGV0ZWQ\/XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwiYm9vbGVhblwiXG4gICAgICB9LFxuICAgICAgXCJ2ZXJzaW9uXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiVmVyc2lvbiBmaWVsZCBmb3IgY29sbGFwc2luZyBsYXRlciAocHN1ZWRvLXRvbWJzdG9uZSlcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgfVxuICB9LFxuICBcInJlcXVpcmVkXCI6IFtcImlkXCIsIFwiY3JlYXRlZF9hdFwiLCBcInRlYW1faWRcIiwgXCJwcm9wZXJ0aWVzXCIsIFwiaXNfaWRlbnRpZmllZFwiLCBcImlzX2RlbGV0ZWRcIiwgXCJ2ZXJzaW9uXCJdXG59XG4iCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2lkbC9wZXJzb25fZGlzdGluY3RfaWQuanNvbgogICAgICAgIHRhcmdldDogL2lkbC9wZXJzb25fZGlzdGluY3RfaWQuanNvbgogICAgICAgIGNvbnRlbnQ6ICJ7XG4gIFwiJHNjaGVtYVwiOiBcImh0dHBzOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LzIwMjAtMTIvc2NoZW1hXCIsXG4gIFwiJGlkXCI6IFwiZmlsZTovL3Bvc3Rob2cvaWRsL3BlcnNvbl9kaXN0aW5jdF9pZC5qc29uXCIsXG4gIFwidGl0bGVcIjogXCJwZXJzb25fZGlzdGluY3RfaWRcIixcbiAgXCJkZXNjcmlwdGlvblwiOiBcIlBlcnNvbiBkaXN0aW5jdCBpZCBzY2hlbWEgdGhhdCBpcyBkZXN0aW5lZCBmb3IgQ2xpY2tIb3VzZVwiLFxuICBcInR5cGVcIjogXCJvYmplY3RcIixcbiAgXCJwcm9wZXJ0aWVzXCI6IHtcbiAgICAgIFwiZGlzdGluY3RfaWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJVc2VyIHByb3ZpZGVkIElEIGZvciB0aGUgZGlzdGluY3QgdXNlclwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICB9LFxuICAgICAgXCJwZXJzb25faWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJVVUlEIG9mIHRoZSBwZXJzb25cIixcbiAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgfSxcbiAgICAgIFwidGVhbV9pZFwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlRlYW0gSUQgYXNzb2NpYXRlZCB3aXRoIHBlcnNvbl9kaXN0aW5jdF9pZFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJfc2lnblwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlVzZWQgZm9yIGNvbGxhcHNpbmcgbGF0ZXIgZGlmZmVyZW50IHZlcnNpb25zIG9mIGEgZGlzdGluY3QgaWQgKHBzdWVkby10b21ic3RvbmUpXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwibnVtYmVyXCJcbiAgICAgIH0sXG4gICAgICBcImlzX2RlbGV0ZWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJCb29sZWFuIGlzIHRoZSBwZXJzb24gZGlzdGluY3RfaWQgZGVsZXRlZD9cIixcbiAgICAgICAgICBcInR5cGVcIjogXCJib29sZWFuXCJcbiAgICAgIH1cbiAgfSxcbiAgXCJyZXF1aXJlZFwiOiBbXCJkaXN0aW5jdF9pZFwiLCBcInBlcnNvbl9pZFwiLCBcInRlYW1faWRcIiwgXCJfc2lnblwiLCBcImlzX2RlbGV0ZWRcIl1cbiB9XG4iCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2lkbC9wZXJzb25fZGlzdGluY3RfaWQyLmpzb24KICAgICAgICB0YXJnZXQ6IC9pZGwvcGVyc29uX2Rpc3RpbmN0X2lkMi5qc29uCiAgICAgICAgY29udGVudDogIntcbiAgICBcIiRzY2hlbWFcIjogXCJodHRwczovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC8yMDIwLTEyL3NjaGVtYVwiLFxuICAgIFwiJGlkXCI6IFwiZmlsZTovL3Bvc3Rob2cvaWRsL3BlcnNvbl9kaXN0aW5jdF9pZDIuanNvblwiLFxuICAgIFwidGl0bGVcIjogXCJwZXJzb25fZGlzdGluY3RfaWQyXCIsXG4gICAgXCJkZXNjcmlwdGlvblwiOiBcIlBlcnNvbiBkaXN0aW5jdCBpZDIgc2NoZW1hIHRoYXQgaXMgZGVzdGluZWQgZm9yIENsaWNrSG91c2VcIixcbiAgICBcInR5cGVcIjogXCJvYmplY3RcIixcbiAgICBcInByb3BlcnRpZXNcIjoge1xuICAgICAgICBcImRpc3RpbmN0X2lkXCI6IHtcbiAgICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJVc2VyIHByb3ZpZGVkIElEIGZvciB0aGUgZGlzdGluY3QgdXNlclwiLFxuICAgICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgICAgfSxcbiAgICAgICAgXCJwZXJzb25faWRcIjoge1xuICAgICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlVVSUQgb2YgdGhlIHBlcnNvblwiLFxuICAgICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgICAgfSxcbiAgICAgICAgXCJ0ZWFtX2lkXCI6IHtcbiAgICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJUZWFtIElEIGFzc29jaWF0ZWQgd2l0aCBwZXJzb25fZGlzdGluY3RfaWRcIixcbiAgICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICAgIH0sXG4gICAgICAgIFwidmVyc2lvblwiOiB7XG4gICAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiVXNlZCBmb3IgY29sbGFwc2luZyBsYXRlciBkaWZmZXJlbnQgdmVyc2lvbnMgb2YgYSBkaXN0aW5jdCBpZCAocHN1ZWRvLXRvbWJzdG9uZSlcIixcbiAgICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICAgIH0sXG4gICAgICAgIFwiaXNfZGVsZXRlZFwiOiB7XG4gICAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiQm9vbGVhbiBpcyB0aGUgcGVyc29uIGRpc3RpbmN0X2lkIGRlbGV0ZWQ\/XCIsXG4gICAgICAgICAgICBcInR5cGVcIjogXCJib29sZWFuXCJcbiAgICAgICAgfVxuICAgIH0sXG4gICAgXCJyZXF1aXJlZFwiOiBbXCJkaXN0aW5jdF9pZFwiLCBcInBlcnNvbl9pZFwiLCBcInRlYW1faWRcIiwgXCJ2ZXJzaW9uXCIsIFwiaXNfZGVsZXRlZFwiXVxufVxuIgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9pZGwvcGx1Z2luX2xvZ19lbnRyaWVzLmpzb24KICAgICAgICB0YXJnZXQ6IC9pZGwvcGx1Z2luX2xvZ19lbnRyaWVzLmpzb24KICAgICAgICBjb250ZW50OiAie1xuICAgIFwiJHNjaGVtYVwiOiBcImh0dHBzOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LzIwMjAtMTIvc2NoZW1hXCIsXG4gICAgXCIkaWRcIjogXCJmaWxlOi8vcG9zdGhvZy9pZGwvcGx1Z2luX2xvZ19lbnRyaWVzLmpzb25cIixcbiAgICBcInRpdGxlXCI6IFwicGx1Z2luX2xvZ19lbnRyaWVzXCIsXG4gICAgXCJkZXNjcmlwdGlvblwiOiBcIlBsdWdpbiBsb2cgZW50cmllcyB0aGF0IGFyZSBkZXN0aW5lZCBmb3IgQ2xpY2tIb3VzZVwiLFxuICAgIFwidHlwZVwiOiBcIm9iamVjdFwiLFxuICAgIFwicHJvcGVydGllc1wiOiB7XG4gICAgICAgIFwiaWRcIjoge1xuICAgICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlVVSUQgZm9yIHRoZSBsb2cgZW50cnlcIixcbiAgICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICAgIH0sXG4gICAgICAgIFwidGVhbV9pZFwiOiB7XG4gICAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiVGVhbSBJRCBhc3NvY2lhdGVkIHdpdGggcGVyc29uX2Rpc3RpbmN0X2lkXCIsXG4gICAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgICB9LFxuICAgICAgICBcInBsdWdpbl9pZFwiOiB7XG4gICAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiUGx1Z2luIElEIGFzc29jaWF0ZWQgd2l0aCB0aGUgbG9nIGVudHJ5XCIsXG4gICAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgICB9LFxuICAgICAgICBcInBsdWdpbl9jb25maWdfaWRcIjoge1xuICAgICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlBsdWdpbiBDb25maWcgSUQgYXNzb2NpYXRlZCB3aXRoIHRoZSBsb2cgZW50cnlcIixcbiAgICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICAgIH0sXG4gICAgICAgIFwidGltZXN0YW1wXCI6IHtcbiAgICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJUaW1lc3RhbXAgZm9yIHdoZW4gdGhlIGxvZyBlbnRyeSB3YXMgY3JlYXRlZFwiLFxuICAgICAgICAgICAgXCJ0eXBlXCI6IFwibnVtYmVyXCJcbiAgICAgICAgfSxcbiAgICAgICAgXCJzb3VyY2VcIjoge1xuICAgICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlNvdXJjZSBvZiB0aGUgbG9nIGVudHJ5XCIsXG4gICAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgICB9LFxuICAgICAgICBcInR5cGVcIjoge1xuICAgICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIkxvZyBlbnRyeSB0eXBlXCIsXG4gICAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgICB9LFxuICAgICAgICBcIm1lc3NhZ2VcIjoge1xuICAgICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIkxvZyBlbnRyeSBib2R5XCIsXG4gICAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgICB9LFxuICAgICAgICBcImluc3RhbmNlX2lkXCI6IHtcbiAgICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJVVUlEIG9mIHRoZSBpbnN0YW5jZSB0aGF0IGdlbmVyYXRlZCB0aGUgbG9nIGVudHJ5XCIsXG4gICAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgICB9XG4gICAgfSxcbiAgICBcInJlcXVpcmVkXCI6IFtcbiAgICAgICAgXCJpZFwiLFxuICAgICAgICBcInRlYW1faWRcIixcbiAgICAgICAgXCJwbHVnaW5faWRcIixcbiAgICAgICAgXCJwbHVnaW5fY29uZmlnX2lkXCIsXG4gICAgICAgIFwidGltZXN0YW1wXCIsXG4gICAgICAgIFwic291cmNlXCIsXG4gICAgICAgIFwidHlwZVwiLFxuICAgICAgICBcIm1lc3NhZ2VcIixcbiAgICAgICAgXCJpbnN0YW5jZV9pZFwiXG4gICAgXVxufVxuIgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9kb2NrZXIvY2xpY2tob3VzZS9kb2NrZXItZW50cnlwb2ludC1pbml0ZGIuZC9pbml0LWRiLnNoCiAgICAgICAgdGFyZ2V0OiAvZG9ja2VyLWVudHJ5cG9pbnQtaW5pdGRiLmQvaW5pdC1kYi5zaAogICAgICAgIGNvbnRlbnQ6ICIjIS9iaW4vYmFzaFxuc2V0IC1lXG5cbmNwIC1yIC9pZGwvKiAvdmFyL2xpYi9jbGlja2hvdXNlL2Zvcm1hdF9zY2hlbWFzL1xuIgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9kb2NrZXIvY2xpY2tob3VzZS9jb25maWcueG1sCiAgICAgICAgdGFyZ2V0OiAvZXRjL2NsaWNraG91c2Utc2VydmVyL2NvbmZpZy54bWwKICAgICAgICBjb250ZW50OiAiPD94bWwgdmVyc2lvbj1cIjEuMFwiPz5cbjwhLS1cbiAgTk9URTogVXNlciBhbmQgcXVlcnkgbGV2ZWwgc2V0dGluZ3MgYXJlIHNldCB1cCBpbiBcInVzZXJzLnhtbFwiIGZpbGUuXG4gIElmIHlvdSBoYXZlIGFjY2lkZW50YWxseSBzcGVjaWZpZWQgdXNlci1sZXZlbCBzZXR0aW5ncyBoZXJlLCBzZXJ2ZXIgd29uJ3Qgc3RhcnQuXG4gIFlvdSBjYW4gZWl0aGVyIG1vdmUgdGhlIHNldHRpbmdzIHRvIHRoZSByaWdodCBwbGFjZSBpbnNpZGUgXCJ1c2Vycy54bWxcIiBmaWxlXG4gIG9yIGFkZCA8c2tpcF9jaGVja19mb3JfaW5jb3JyZWN0X3NldHRpbmdzPjE8L3NraXBfY2hlY2tfZm9yX2luY29ycmVjdF9zZXR0aW5ncz4gaGVyZS5cbi0tPlxuPHlhbmRleD5cbiAgICA8bG9nZ2VyPlxuICAgICAgICA8IS0tIFBvc3NpYmxlIGxldmVscyBbMV06XG5cbiAgICAgICAgICAtIG5vbmUgKHR1cm5zIG9mZiBsb2dnaW5nKVxuICAgICAgICAgIC0gZmF0YWxcbiAgICAgICAgICAtIGNyaXRpY2FsXG4gICAgICAgICAgLSBlcnJvclxuICAgICAgICAgIC0gd2FybmluZ1xuICAgICAgICAgIC0gbm90aWNlXG4gICAgICAgICAgLSBpbmZvcm1hdGlvblxuICAgICAgICAgIC0gZGVidWdcbiAgICAgICAgICAtIHRyYWNlXG4gICAgICAgICAgLSB0ZXN0IChub3QgZm9yIHByb2R1Y3Rpb24gdXNhZ2UpXG5cbiAgICAgICAgICAgIFsxXTpcbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL3BvY29wcm9qZWN0L3BvY28vYmxvYi9wb2NvLTEuOS40LXJlbGVhc2UvRm91bmRhdGlvbi9pbmNsdWRlL1BvY28vTG9nZ2VyLmgjTDEwNS1MMTE0XG4gICAgICAgIC0tPlxuICAgICAgICA8bGV2ZWw+dHJhY2U8L2xldmVsPlxuICAgICAgICA8bG9nPi92YXIvbG9nL2NsaWNraG91c2Utc2VydmVyL2NsaWNraG91c2Utc2VydmVyLmxvZzwvbG9nPlxuICAgICAgICA8ZXJyb3Jsb2c+L3Zhci9sb2cvY2xpY2tob3VzZS1zZXJ2ZXIvY2xpY2tob3VzZS1zZXJ2ZXIuZXJyLmxvZzwvZXJyb3Jsb2c+XG4gICAgICAgIDwhLS0gUm90YXRpb24gcG9saWN5XG4gICAgICAgICAgICBTZWVcbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL3BvY29wcm9qZWN0L3BvY28vYmxvYi9wb2NvLTEuOS40LXJlbGVhc2UvRm91bmRhdGlvbi9pbmNsdWRlL1BvY28vRmlsZUNoYW5uZWwuaCNMNTQtTDg1XG4gICAgICAgICAgLS0+XG4gICAgICAgIDxzaXplPjEwMDBNPC9zaXplPlxuICAgICAgICA8Y291bnQ+MTA8L2NvdW50PlxuICAgICAgICA8IS0tIDxjb25zb2xlPjE8L2NvbnNvbGU+IC0tPiA8IS0tIERlZmF1bHQgYmVoYXZpb3IgaXMgYXV0b2RldGVjdGlvbiAobG9nIHRvIGNvbnNvbGUgaWYgbm90IGRhZW1vbiBtb2RlXG4gICAgICAgIGFuZCBpcyB0dHkpIC0tPlxuXG4gICAgICAgIDwhLS0gUGVyIGxldmVsIG92ZXJyaWRlcyAobGVnYWN5KTpcblxuICAgICAgICBGb3IgZXhhbXBsZSB0byBzdXBwcmVzcyBsb2dnaW5nIG9mIHRoZSBDb25maWdSZWxvYWRlciB5b3UgY2FuIHVzZTpcbiAgICAgICAgTk9URTogbGV2ZWxzLmxvZ2dlciBpcyByZXNlcnZlZCwgc2VlIGJlbG93LlxuICAgICAgICAtLT5cbiAgICAgICAgPCEtLVxuICAgICAgICA8bGV2ZWxzPlxuICAgICAgICAgIDxDb25maWdSZWxvYWRlcj5ub25lPC9Db25maWdSZWxvYWRlcj5cbiAgICAgICAgPC9sZXZlbHM+XG4gICAgICAgIC0tPlxuXG4gICAgICAgIDwhLS0gUGVyIGxldmVsIG92ZXJyaWRlczpcblxuICAgICAgICBGb3IgZXhhbXBsZSB0byBzdXBwcmVzcyBsb2dnaW5nIG9mIHRoZSBSQkFDIGZvciBkZWZhdWx0IHVzZXIgeW91IGNhbiB1c2U6XG4gICAgICAgIChCdXQgcGxlYXNlIG5vdGUgdGhhdCB0aGUgbG9nZ2VyIG5hbWUgbWF5YmUgY2hhbmdlZCBmcm9tIHZlcnNpb24gdG8gdmVyc2lvbiwgZXZlbiBhZnRlciBtaW5vclxuICAgICAgICB1cGdyYWRlKVxuICAgICAgICAtLT5cbiAgICAgICAgPCEtLVxuICAgICAgICA8bGV2ZWxzPlxuICAgICAgICAgIDxsb2dnZXI+XG4gICAgICAgICAgICA8bmFtZT5Db250ZXh0QWNjZXNzIChkZWZhdWx0KTwvbmFtZT5cbiAgICAgICAgICAgIDxsZXZlbD5ub25lPC9sZXZlbD5cbiAgICAgICAgICA8L2xvZ2dlcj5cbiAgICAgICAgICA8bG9nZ2VyPlxuICAgICAgICAgICAgPG5hbWU+RGF0YWJhc2VPcmRpbmFyeSAodGVzdCk8L25hbWU+XG4gICAgICAgICAgICA8bGV2ZWw+bm9uZTwvbGV2ZWw+XG4gICAgICAgICAgPC9sb2dnZXI+XG4gICAgICAgIDwvbGV2ZWxzPlxuICAgICAgICAtLT5cbiAgICA8L2xvZ2dlcj5cblxuICAgIDwhLS0gQWRkIGhlYWRlcnMgdG8gcmVzcG9uc2UgaW4gb3B0aW9ucyByZXF1ZXN0LiBPUFRJT05TIG1ldGhvZCBpcyB1c2VkIGluIENPUlMgcHJlZmxpZ2h0XG4gICAgcmVxdWVzdHMuIC0tPlxuICAgIDwhLS0gSXQgaXMgb2ZmIGJ5IGRlZmF1bHQuIE5leHQgaGVhZGVycyBhcmUgb2JsaWdhdGUgZm9yIENPUlMuLS0+XG4gICAgPCEtLSBodHRwX29wdGlvbnNfcmVzcG9uc2U+XG4gICAgICAgIDxoZWFkZXI+XG4gICAgICAgICAgICA8bmFtZT5BY2Nlc3MtQ29udHJvbC1BbGxvdy1PcmlnaW48L25hbWU+XG4gICAgICAgICAgICA8dmFsdWU+KjwvdmFsdWU+XG4gICAgICAgIDwvaGVhZGVyPlxuICAgICAgICA8aGVhZGVyPlxuICAgICAgICAgICAgPG5hbWU+QWNjZXNzLUNvbnRyb2wtQWxsb3ctSGVhZGVyczwvbmFtZT5cbiAgICAgICAgICAgIDx2YWx1ZT5vcmlnaW4sIHgtcmVxdWVzdGVkLXdpdGg8L3ZhbHVlPlxuICAgICAgICA8L2hlYWRlcj5cbiAgICAgICAgPGhlYWRlcj5cbiAgICAgICAgICAgIDxuYW1lPkFjY2Vzcy1Db250cm9sLUFsbG93LU1ldGhvZHM8L25hbWU+XG4gICAgICAgICAgICA8dmFsdWU+UE9TVCwgR0VULCBPUFRJT05TPC92YWx1ZT5cbiAgICAgICAgPC9oZWFkZXI+XG4gICAgICAgIDxoZWFkZXI+XG4gICAgICAgICAgICA8bmFtZT5BY2Nlc3MtQ29udHJvbC1NYXgtQWdlPC9uYW1lPlxuICAgICAgICAgICAgPHZhbHVlPjg2NDAwPC92YWx1ZT5cbiAgICAgICAgPC9oZWFkZXI+XG4gICAgPC9odHRwX29wdGlvbnNfcmVzcG9uc2UgLS0+XG5cbiAgICA8IS0tIEl0IGlzIHRoZSBuYW1lIHRoYXQgd2lsbCBiZSBzaG93biBpbiB0aGUgY2xpY2tob3VzZS1jbGllbnQuXG4gICAgICAgIEJ5IGRlZmF1bHQsIGFueXRoaW5nIHdpdGggXCJwcm9kdWN0aW9uXCIgd2lsbCBiZSBoaWdobGlnaHRlZCBpbiByZWQgaW4gcXVlcnkgcHJvbXB0LlxuICAgIC0tPlxuICAgIDwhLS1kaXNwbGF5X25hbWU+cHJvZHVjdGlvbjwvZGlzcGxheV9uYW1lLS0+XG5cbiAgICA8IS0tIFBvcnQgZm9yIEhUVFAgQVBJLiBTZWUgYWxzbyAnaHR0cHNfcG9ydCcgZm9yIHNlY3VyZSBjb25uZWN0aW9ucy5cbiAgICAgICAgVGhpcyBpbnRlcmZhY2UgaXMgYWxzbyB1c2VkIGJ5IE9EQkMgYW5kIEpEQkMgZHJpdmVycyAoRGF0YUdyaXAsIERiZWF2ZXIsIC4uLilcbiAgICAgICAgYW5kIGJ5IG1vc3Qgb2Ygd2ViIGludGVyZmFjZXMgKGVtYmVkZGVkIFVJLCBHcmFmYW5hLCBSZWRhc2gsIC4uLikuXG4gICAgICAtLT5cbiAgICA8aHR0cF9wb3J0PjgxMjM8L2h0dHBfcG9ydD5cblxuICAgIDwhLS0gUG9ydCBmb3IgaW50ZXJhY3Rpb24gYnkgbmF0aXZlIHByb3RvY29sIHdpdGg6XG4gICAgICAgIC0gY2xpY2tob3VzZS1jbGllbnQgYW5kIG90aGVyIG5hdGl2ZSBDbGlja0hvdXNlIHRvb2xzIChjbGlja2hvdXNlLWJlbmNobWFyaywgY2xpY2tob3VzZS1jb3BpZXIpO1xuICAgICAgICAtIGNsaWNraG91c2Utc2VydmVyIHdpdGggb3RoZXIgY2xpY2tob3VzZS1zZXJ2ZXJzIGZvciBkaXN0cmlidXRlZCBxdWVyeSBwcm9jZXNzaW5nO1xuICAgICAgICAtIENsaWNrSG91c2UgZHJpdmVycyBhbmQgYXBwbGljYXRpb25zIHN1cHBvcnRpbmcgbmF0aXZlIHByb3RvY29sXG4gICAgICAgICh0aGlzIHByb3RvY29sIGlzIGFsc28gaW5mb3JtYWxseSBjYWxsZWQgYXMgXCJ0aGUgVENQIHByb3RvY29sXCIpO1xuICAgICAgICBTZWUgYWxzbyAndGNwX3BvcnRfc2VjdXJlJyBmb3Igc2VjdXJlIGNvbm5lY3Rpb25zLlxuICAgIC0tPlxuICAgIDx0Y3BfcG9ydD45MDAwPC90Y3BfcG9ydD5cblxuICAgIDwhLS0gQ29tcGF0aWJpbGl0eSB3aXRoIE15U1FMIHByb3RvY29sLlxuICAgICAgICBDbGlja0hvdXNlIHdpbGwgcHJldGVuZCB0byBiZSBNeVNRTCBmb3IgYXBwbGljYXRpb25zIGNvbm5lY3RpbmcgdG8gdGhpcyBwb3J0LlxuICAgIC0tPlxuICAgIDxteXNxbF9wb3J0PjkwMDQ8L215c3FsX3BvcnQ+XG5cbiAgICA8IS0tIENvbXBhdGliaWxpdHkgd2l0aCBQb3N0Z3JlU1FMIHByb3RvY29sLlxuICAgICAgICBDbGlja0hvdXNlIHdpbGwgcHJldGVuZCB0byBiZSBQb3N0Z3JlU1FMIGZvciBhcHBsaWNhdGlvbnMgY29ubmVjdGluZyB0byB0aGlzIHBvcnQuXG4gICAgLS0+XG4gICAgPHBvc3RncmVzcWxfcG9ydD45MDA1PC9wb3N0Z3Jlc3FsX3BvcnQ+XG5cbiAgICA8IS0tIEhUVFAgQVBJIHdpdGggVExTIChIVFRQUykuXG4gICAgICAgIFlvdSBoYXZlIHRvIGNvbmZpZ3VyZSBjZXJ0aWZpY2F0ZSB0byBlbmFibGUgdGhpcyBpbnRlcmZhY2UuXG4gICAgICAgIFNlZSB0aGUgb3BlblNTTCBzZWN0aW9uIGJlbG93LlxuICAgIC0tPlxuICAgIDxodHRwc19wb3J0Pjg0NDM8L2h0dHBzX3BvcnQ+XG5cbiAgICA8IS0tIE5hdGl2ZSBpbnRlcmZhY2Ugd2l0aCBUTFMuXG4gICAgICAgIFlvdSBoYXZlIHRvIGNvbmZpZ3VyZSBjZXJ0aWZpY2F0ZSB0byBlbmFibGUgdGhpcyBpbnRlcmZhY2UuXG4gICAgICAgIFNlZSB0aGUgb3BlblNTTCBzZWN0aW9uIGJlbG93LlxuICAgIC0tPlxuICAgIDx0Y3BfcG9ydF9zZWN1cmU+OTQ0MDwvdGNwX3BvcnRfc2VjdXJlPlxuXG4gICAgPCEtLSBOYXRpdmUgaW50ZXJmYWNlIHdyYXBwZWQgd2l0aCBQUk9YWXYxIHByb3RvY29sXG4gICAgICAgIFBST1hZdjEgaGVhZGVyIHNlbnQgZm9yIGV2ZXJ5IGNvbm5lY3Rpb24uXG4gICAgICAgIENsaWNrSG91c2Ugd2lsbCBleHRyYWN0IGluZm9ybWF0aW9uIGFib3V0IHByb3h5LWZvcndhcmRlZCBjbGllbnQgYWRkcmVzcyBmcm9tIHRoZSBoZWFkZXIuXG4gICAgLS0+XG4gICAgPCEtLSA8dGNwX3dpdGhfcHJveHlfcG9ydD45MDExPC90Y3Bfd2l0aF9wcm94eV9wb3J0PiAtLT5cblxuICAgIDwhLS0gUG9ydCBmb3IgY29tbXVuaWNhdGlvbiBiZXR3ZWVuIHJlcGxpY2FzLiBVc2VkIGZvciBkYXRhIGV4Y2hhbmdlLlxuICAgICAgICBJdCBwcm92aWRlcyBsb3ctbGV2ZWwgZGF0YSBhY2Nlc3MgYmV0d2VlbiBzZXJ2ZXJzLlxuICAgICAgICBUaGlzIHBvcnQgc2hvdWxkIG5vdCBiZSBhY2Nlc3NpYmxlIGZyb20gdW50cnVzdGVkIG5ldHdvcmtzLlxuICAgICAgICBTZWUgYWxzbyAnaW50ZXJzZXJ2ZXJfaHR0cF9jcmVkZW50aWFscycuXG4gICAgICAgIERhdGEgdHJhbnNmZXJyZWQgb3ZlciBjb25uZWN0aW9ucyB0byB0aGlzIHBvcnQgc2hvdWxkIG5vdCBnbyB0aHJvdWdoIHVudHJ1c3RlZCBuZXR3b3Jrcy5cbiAgICAgICAgU2VlIGFsc28gJ2ludGVyc2VydmVyX2h0dHBzX3BvcnQnLlxuICAgICAgLS0+XG4gICAgPGludGVyc2VydmVyX2h0dHBfcG9ydD45MDA5PC9pbnRlcnNlcnZlcl9odHRwX3BvcnQ+XG5cbiAgICA8IS0tIFBvcnQgZm9yIGNvbW11bmljYXRpb24gYmV0d2VlbiByZXBsaWNhcyB3aXRoIFRMUy5cbiAgICAgICAgWW91IGhhdmUgdG8gY29uZmlndXJlIGNlcnRpZmljYXRlIHRvIGVuYWJsZSB0aGlzIGludGVyZmFjZS5cbiAgICAgICAgU2VlIHRoZSBvcGVuU1NMIHNlY3Rpb24gYmVsb3cuXG4gICAgICAgIFNlZSBhbHNvICdpbnRlcnNlcnZlcl9odHRwX2NyZWRlbnRpYWxzJy5cbiAgICAgIC0tPlxuICAgIDwhLS0gPGludGVyc2VydmVyX2h0dHBzX3BvcnQ+OTAxMDwvaW50ZXJzZXJ2ZXJfaHR0cHNfcG9ydD4gLS0+XG5cbiAgICA8IS0tIEhvc3RuYW1lIHRoYXQgaXMgdXNlZCBieSBvdGhlciByZXBsaWNhcyB0byByZXF1ZXN0IHRoaXMgc2VydmVyLlxuICAgICAgICBJZiBub3Qgc3BlY2lmaWVkLCB0aGFuIGl0IGlzIGRldGVybWluZWQgYW5hbG9nb3VzIHRvICdob3N0bmFtZSAtZicgY29tbWFuZC5cbiAgICAgICAgVGhpcyBzZXR0aW5nIGNvdWxkIGJlIHVzZWQgdG8gc3dpdGNoIHJlcGxpY2F0aW9uIHRvIGFub3RoZXIgbmV0d29yayBpbnRlcmZhY2VcbiAgICAgICAgKHRoZSBzZXJ2ZXIgbWF5IGJlIGNvbm5lY3RlZCB0byBtdWx0aXBsZSBuZXR3b3JrcyB2aWEgbXVsdGlwbGUgYWRkcmVzc2VzKVxuICAgICAgLS0+XG5cbiAgICA8IS0tXG4gICAgPGludGVyc2VydmVyX2h0dHBfaG9zdD5leGFtcGxlLnlhbmRleC5ydTwvaW50ZXJzZXJ2ZXJfaHR0cF9ob3N0PlxuICAgIC0tPlxuXG4gICAgPCEtLSBZb3UgY2FuIHNwZWNpZnkgY3JlZGVudGlhbHMgZm9yIGF1dGhlbnRoaWNhdGlvbiBiZXR3ZWVuIHJlcGxpY2FzLlxuICAgICAgICBUaGlzIGlzIHJlcXVpcmVkIHdoZW4gaW50ZXJzZXJ2ZXJfaHR0cHNfcG9ydCBpcyBhY2Nlc3NpYmxlIGZyb20gdW50cnVzdGVkIG5ldHdvcmtzLFxuICAgICAgICBhbmQgYWxzbyByZWNvbW1lbmRlZCB0byBhdm9pZCBTU1JGIGF0dGFja3MgZnJvbSBwb3NzaWJseSBjb21wcm9taXNlZCBzZXJ2aWNlcyBpbiB5b3VyIG5ldHdvcmsuXG4gICAgICAtLT5cbiAgICA8IS0tPGludGVyc2VydmVyX2h0dHBfY3JlZGVudGlhbHM+XG4gICAgICAgIDx1c2VyPmludGVyc2VydmVyPC91c2VyPlxuICAgICAgICA8cGFzc3dvcmQ+PC9wYXNzd29yZD5cbiAgICA8L2ludGVyc2VydmVyX2h0dHBfY3JlZGVudGlhbHM+LS0+XG5cbiAgICA8IS0tIExpc3RlbiBzcGVjaWZpZWQgYWRkcmVzcy5cbiAgICAgICAgVXNlIDo6ICh3aWxkY2FyZCBJUHY2IGFkZHJlc3MpLCBpZiB5b3Ugd2FudCB0byBhY2NlcHQgY29ubmVjdGlvbnMgYm90aCB3aXRoIElQdjQgYW5kIElQdjYgZnJvbVxuICAgIGV2ZXJ5d2hlcmUuXG4gICAgICAgIE5vdGVzOlxuICAgICAgICBJZiB5b3Ugb3BlbiBjb25uZWN0aW9ucyBmcm9tIHdpbGRjYXJkIGFkZHJlc3MsIG1ha2Ugc3VyZSB0aGF0IGF0IGxlYXN0IG9uZSBvZiB0aGUgZm9sbG93aW5nXG4gICAgbWVhc3VyZXMgYXBwbGllZDpcbiAgICAgICAgLSBzZXJ2ZXIgaXMgcHJvdGVjdGVkIGJ5IGZpcmV3YWxsIGFuZCBub3QgYWNjZXNzaWJsZSBmcm9tIHVudHJ1c3RlZCBuZXR3b3JrcztcbiAgICAgICAgLSBhbGwgdXNlcnMgYXJlIHJlc3RyaWN0ZWQgdG8gc3Vic2V0IG9mIG5ldHdvcmsgYWRkcmVzc2VzIChzZWUgdXNlcnMueG1sKTtcbiAgICAgICAgLSBhbGwgdXNlcnMgaGF2ZSBzdHJvbmcgcGFzc3dvcmRzLCBvbmx5IHNlY3VyZSAoVExTKSBpbnRlcmZhY2VzIGFyZSBhY2Nlc3NpYmxlLCBvciBjb25uZWN0aW9ucyBhcmVcbiAgICBvbmx5IG1hZGUgdmlhIFRMUyBpbnRlcmZhY2VzLlxuICAgICAgICAtIHVzZXJzIHdpdGhvdXQgcGFzc3dvcmQgaGF2ZSByZWFkb25seSBhY2Nlc3MuXG4gICAgICAgIFNlZSBhbHNvOiBodHRwczovL3d3dy5zaG9kYW4uaW8vc2VhcmNoP3F1ZXJ5PWNsaWNraG91c2VcbiAgICAgIC0tPlxuICAgIDwhLS0gPGxpc3Rlbl9ob3N0Pjo6PC9saXN0ZW5faG9zdD4gLS0+XG5cblxuICAgIDwhLS0gU2FtZSBmb3IgaG9zdHMgd2l0aG91dCBzdXBwb3J0IGZvciBJUHY2OiAtLT5cbiAgICA8IS0tIDxsaXN0ZW5faG9zdD4wLjAuMC4wPC9saXN0ZW5faG9zdD4gLS0+XG5cbiAgICA8IS0tIERlZmF1bHQgdmFsdWVzIC0gdHJ5IGxpc3RlbiBsb2NhbGhvc3Qgb24gSVB2NCBhbmQgSVB2Ni4gLS0+XG4gICAgPCEtLVxuICAgIDxsaXN0ZW5faG9zdD46OjE8L2xpc3Rlbl9ob3N0PlxuICAgIDxsaXN0ZW5faG9zdD4xMjcuMC4wLjE8L2xpc3Rlbl9ob3N0PlxuICAgIC0tPlxuXG4gICAgPCEtLSBEb24ndCBleGl0IGlmIElQdjYgb3IgSVB2NCBuZXR3b3JrcyBhcmUgdW5hdmFpbGFibGUgd2hpbGUgdHJ5aW5nIHRvIGxpc3Rlbi4gLS0+XG4gICAgPCEtLSA8bGlzdGVuX3RyeT4wPC9saXN0ZW5fdHJ5PiAtLT5cblxuICAgIDwhLS0gQWxsb3cgbXVsdGlwbGUgc2VydmVycyB0byBsaXN0ZW4gb24gdGhlIHNhbWUgYWRkcmVzczpwb3J0LiBUaGlzIGlzIG5vdCByZWNvbW1lbmRlZC5cbiAgICAgIC0tPlxuICAgIDwhLS0gPGxpc3Rlbl9yZXVzZV9wb3J0PjA8L2xpc3Rlbl9yZXVzZV9wb3J0PiAtLT5cblxuICAgIDwhLS0gPGxpc3Rlbl9iYWNrbG9nPjQwOTY8L2xpc3Rlbl9iYWNrbG9nPiAtLT5cblxuICAgIDxtYXhfY29ubmVjdGlvbnM+NDA5NjwvbWF4X2Nvbm5lY3Rpb25zPlxuXG4gICAgPCEtLSBGb3IgJ0Nvbm5lY3Rpb246IGtlZXAtYWxpdmUnIGluIEhUVFAgMS4xIC0tPlxuICAgIDxrZWVwX2FsaXZlX3RpbWVvdXQ+Mzwva2VlcF9hbGl2ZV90aW1lb3V0PlxuXG4gICAgPCEtLSBnUlBDIHByb3RvY29sIChzZWUgc3JjL1NlcnZlci9ncnBjX3Byb3Rvcy9jbGlja2hvdXNlX2dycGMucHJvdG8gZm9yIHRoZSBBUEkpIC0tPlxuICAgIDwhLS0gPGdycGNfcG9ydD45MTAwPC9ncnBjX3BvcnQ+IC0tPlxuICAgIDxncnBjPlxuICAgICAgICA8ZW5hYmxlX3NzbD5mYWxzZTwvZW5hYmxlX3NzbD5cblxuICAgICAgICA8IS0tIFRoZSBmb2xsb3dpbmcgdHdvIGZpbGVzIGFyZSB1c2VkIG9ubHkgaWYgZW5hYmxlX3NzbD0xIC0tPlxuICAgICAgICA8c3NsX2NlcnRfZmlsZT4vcGF0aC90by9zc2xfY2VydF9maWxlPC9zc2xfY2VydF9maWxlPlxuICAgICAgICA8c3NsX2tleV9maWxlPi9wYXRoL3RvL3NzbF9rZXlfZmlsZTwvc3NsX2tleV9maWxlPlxuXG4gICAgICAgIDwhLS0gV2hldGhlciBzZXJ2ZXIgd2lsbCByZXF1ZXN0IGNsaWVudCBmb3IgYSBjZXJ0aWZpY2F0ZSAtLT5cbiAgICAgICAgPHNzbF9yZXF1aXJlX2NsaWVudF9hdXRoPmZhbHNlPC9zc2xfcmVxdWlyZV9jbGllbnRfYXV0aD5cblxuICAgICAgICA8IS0tIFRoZSBmb2xsb3dpbmcgZmlsZSBpcyB1c2VkIG9ubHkgaWYgc3NsX3JlcXVpcmVfY2xpZW50X2F1dGg9MSAtLT5cbiAgICAgICAgPHNzbF9jYV9jZXJ0X2ZpbGU+L3BhdGgvdG8vc3NsX2NhX2NlcnRfZmlsZTwvc3NsX2NhX2NlcnRfZmlsZT5cblxuICAgICAgICA8IS0tIERlZmF1bHQgdHJhbnNwb3J0IGNvbXByZXNzaW9uIHR5cGUgKGNhbiBiZSBvdmVycmlkZGVuIGJ5IGNsaWVudCwgc2VlIHRoZVxuICAgICAgICB0cmFuc3BvcnRfY29tcHJlc3Npb25fdHlwZSBmaWVsZCBpbiBRdWVyeUluZm8pLlxuICAgICAgICAgICAgU3VwcG9ydGVkIGFsZ29yaXRobXM6IG5vbmUsIGRlZmxhdGUsIGd6aXAsIHN0cmVhbV9nemlwIC0tPlxuICAgICAgICA8dHJhbnNwb3J0X2NvbXByZXNzaW9uX3R5cGU+bm9uZTwvdHJhbnNwb3J0X2NvbXByZXNzaW9uX3R5cGU+XG5cbiAgICAgICAgPCEtLSBEZWZhdWx0IHRyYW5zcG9ydCBjb21wcmVzc2lvbiBsZXZlbC4gU3VwcG9ydGVkIGxldmVsczogMC4uMyAtLT5cbiAgICAgICAgPHRyYW5zcG9ydF9jb21wcmVzc2lvbl9sZXZlbD4wPC90cmFuc3BvcnRfY29tcHJlc3Npb25fbGV2ZWw+XG5cbiAgICAgICAgPCEtLSBTZW5kL3JlY2VpdmUgbWVzc2FnZSBzaXplIGxpbWl0cyBpbiBieXRlcy4gLTEgbWVhbnMgdW5saW1pdGVkIC0tPlxuICAgICAgICA8bWF4X3NlbmRfbWVzc2FnZV9zaXplPi0xPC9tYXhfc2VuZF9tZXNzYWdlX3NpemU+XG4gICAgICAgIDxtYXhfcmVjZWl2ZV9tZXNzYWdlX3NpemU+LTE8L21heF9yZWNlaXZlX21lc3NhZ2Vfc2l6ZT5cblxuICAgICAgICA8IS0tIEVuYWJsZSBpZiB5b3Ugd2FudCB2ZXJ5IGRldGFpbGVkIGxvZ3MgLS0+XG4gICAgICAgIDx2ZXJib3NlX2xvZ3M+ZmFsc2U8L3ZlcmJvc2VfbG9ncz5cbiAgICA8L2dycGM+XG5cbiAgICA8IS0tIFVzZWQgd2l0aCBodHRwc19wb3J0IGFuZCB0Y3BfcG9ydF9zZWN1cmUuIEZ1bGwgc3NsIG9wdGlvbnMgbGlzdDpcbiAgICBodHRwczovL2dpdGh1Yi5jb20vQ2xpY2tIb3VzZS1FeHRyYXMvcG9jby9ibG9iL21hc3Rlci9OZXRTU0xfT3BlblNTTC9pbmNsdWRlL1BvY28vTmV0L1NTTE1hbmFnZXIuaCNMNzEgLS0+XG4gICAgPG9wZW5TU0w+XG4gICAgICAgIDxzZXJ2ZXI+IDwhLS0gVXNlZCBmb3IgaHR0cHMgc2VydmVyIEFORCBzZWN1cmUgdGNwIHBvcnQgLS0+XG4gICAgICAgICAgICA8IS0tIG9wZW5zc2wgcmVxIC1zdWJqIFwiL0NOPWxvY2FsaG9zdFwiIC1uZXcgLW5ld2tleSByc2E6MjA0OCAtZGF5cyAzNjUgLW5vZGVzIC14NTA5XG4gICAgICAgICAgICAta2V5b3V0IC9ldGMvY2xpY2tob3VzZS1zZXJ2ZXIvc2VydmVyLmtleSAtb3V0IC9ldGMvY2xpY2tob3VzZS1zZXJ2ZXIvc2VydmVyLmNydCAtLT5cbiAgICAgICAgICAgIDxjZXJ0aWZpY2F0ZUZpbGU+L2V0Yy9jbGlja2hvdXNlLXNlcnZlci9zZXJ2ZXIuY3J0PC9jZXJ0aWZpY2F0ZUZpbGU+XG4gICAgICAgICAgICA8cHJpdmF0ZUtleUZpbGU+L2V0Yy9jbGlja2hvdXNlLXNlcnZlci9zZXJ2ZXIua2V5PC9wcml2YXRlS2V5RmlsZT5cbiAgICAgICAgICAgIDwhLS0gZGhwYXJhbXMgYXJlIG9wdGlvbmFsLiBZb3UgY2FuIGRlbGV0ZSB0aGUgPGRoUGFyYW1zRmlsZT4gZWxlbWVudC5cbiAgICAgICAgICAgICAgICBUbyBnZW5lcmF0ZSBkaHBhcmFtcywgdXNlIHRoZSBmb2xsb3dpbmcgY29tbWFuZDpcbiAgICAgICAgICAgICAgICAgIG9wZW5zc2wgZGhwYXJhbSAtb3V0IC9ldGMvY2xpY2tob3VzZS1zZXJ2ZXIvZGhwYXJhbS5wZW0gNDA5NlxuICAgICAgICAgICAgICAgIE9ubHkgZmlsZSBmb3JtYXQgd2l0aCBCRUdJTiBESCBQQVJBTUVURVJTIGlzIHN1cHBvcnRlZC5cbiAgICAgICAgICAgICAgLS0+XG4gICAgICAgICAgICA8ZGhQYXJhbXNGaWxlPi9ldGMvY2xpY2tob3VzZS1zZXJ2ZXIvZGhwYXJhbS5wZW08L2RoUGFyYW1zRmlsZT5cbiAgICAgICAgICAgIDx2ZXJpZmljYXRpb25Nb2RlPm5vbmU8L3ZlcmlmaWNhdGlvbk1vZGU+XG4gICAgICAgICAgICA8bG9hZERlZmF1bHRDQUZpbGU+dHJ1ZTwvbG9hZERlZmF1bHRDQUZpbGU+XG4gICAgICAgICAgICA8Y2FjaGVTZXNzaW9ucz50cnVlPC9jYWNoZVNlc3Npb25zPlxuICAgICAgICAgICAgPGRpc2FibGVQcm90b2NvbHM+c3NsdjIsc3NsdjM8L2Rpc2FibGVQcm90b2NvbHM+XG4gICAgICAgICAgICA8cHJlZmVyU2VydmVyQ2lwaGVycz50cnVlPC9wcmVmZXJTZXJ2ZXJDaXBoZXJzPlxuICAgICAgICA8L3NlcnZlcj5cblxuICAgICAgICA8Y2xpZW50PiA8IS0tIFVzZWQgZm9yIGNvbm5lY3RpbmcgdG8gaHR0cHMgZGljdGlvbmFyeSBzb3VyY2UgYW5kIHNlY3VyZWQgWm9va2VlcGVyXG4gICAgICAgICAgICBjb21tdW5pY2F0aW9uIC0tPlxuICAgICAgICAgICAgPGxvYWREZWZhdWx0Q0FGaWxlPnRydWU8L2xvYWREZWZhdWx0Q0FGaWxlPlxuICAgICAgICAgICAgPGNhY2hlU2Vzc2lvbnM+dHJ1ZTwvY2FjaGVTZXNzaW9ucz5cbiAgICAgICAgICAgIDxkaXNhYmxlUHJvdG9jb2xzPnNzbHYyLHNzbHYzPC9kaXNhYmxlUHJvdG9jb2xzPlxuICAgICAgICAgICAgPHByZWZlclNlcnZlckNpcGhlcnM+dHJ1ZTwvcHJlZmVyU2VydmVyQ2lwaGVycz5cbiAgICAgICAgICAgIDwhLS0gVXNlIGZvciBzZWxmLXNpZ25lZDogPHZlcmlmaWNhdGlvbk1vZGU+bm9uZTwvdmVyaWZpY2F0aW9uTW9kZT4gLS0+XG4gICAgICAgICAgICA8aW52YWxpZENlcnRpZmljYXRlSGFuZGxlcj5cbiAgICAgICAgICAgICAgICA8IS0tIFVzZSBmb3Igc2VsZi1zaWduZWQ6IDxuYW1lPkFjY2VwdENlcnRpZmljYXRlSGFuZGxlcjwvbmFtZT4gLS0+XG4gICAgICAgICAgICAgICAgPG5hbWU+UmVqZWN0Q2VydGlmaWNhdGVIYW5kbGVyPC9uYW1lPlxuICAgICAgICAgICAgPC9pbnZhbGlkQ2VydGlmaWNhdGVIYW5kbGVyPlxuICAgICAgICA8L2NsaWVudD5cbiAgICA8L29wZW5TU0w+XG5cbiAgICA8IS0tIERlZmF1bHQgcm9vdCBwYWdlIG9uIGh0dHBbc10gc2VydmVyLiBGb3IgZXhhbXBsZSBsb2FkIFVJIGZyb20gaHR0cHM6Ly90YWJpeC5pby8gd2hlblxuICAgIG9wZW5pbmcgaHR0cDovL2xvY2FsaG9zdDo4MTIzIC0tPlxuICAgIDwhLS1cbiAgICA8aHR0cF9zZXJ2ZXJfZGVmYXVsdF9yZXNwb25zZT48IVtDREFUQVs8aHRtbCBuZy1hcHA9XCJTTUkyXCI+PGhlYWQ+PGJhc2VcbiAgICBocmVmPVwiaHR0cDovL3VpLnRhYml4LmlvL1wiPjwvaGVhZD48Ym9keT48ZGl2IHVpLXZpZXc9XCJcIiBjbGFzcz1cImNvbnRlbnQtdWlcIj48L2Rpdj48c2NyaXB0XG4gICAgc3JjPVwiaHR0cDovL2xvYWRlci50YWJpeC5pby9tYXN0ZXIuanNcIj48L3NjcmlwdD48L2JvZHk+PC9odG1sPl1dPjwvaHR0cF9zZXJ2ZXJfZGVmYXVsdF9yZXNwb25zZT5cbiAgICAtLT5cblxuICAgIDwhLS0gTWF4aW11bSBudW1iZXIgb2YgY29uY3VycmVudCBxdWVyaWVzLiAtLT5cbiAgICA8bWF4X2NvbmN1cnJlbnRfcXVlcmllcz4xMDA8L21heF9jb25jdXJyZW50X3F1ZXJpZXM+XG5cbiAgICA8IS0tIE1heGltdW0gbWVtb3J5IHVzYWdlIChyZXNpZGVudCBzZXQgc2l6ZSkgZm9yIHNlcnZlciBwcm9jZXNzLlxuICAgICAgICBaZXJvIHZhbHVlIG9yIHVuc2V0IG1lYW5zIGRlZmF1bHQuIERlZmF1bHQgaXMgXCJtYXhfc2VydmVyX21lbW9yeV91c2FnZV90b19yYW1fcmF0aW9cIiBvZiBhdmFpbGFibGVcbiAgICBwaHlzaWNhbCBSQU0uXG4gICAgICAgIElmIHRoZSB2YWx1ZSBpcyBsYXJnZXIgdGhhbiBcIm1heF9zZXJ2ZXJfbWVtb3J5X3VzYWdlX3RvX3JhbV9yYXRpb1wiIG9mIGF2YWlsYWJsZSBwaHlzaWNhbCBSQU0sIGl0XG4gICAgd2lsbCBiZSBjdXQgZG93bi5cblxuICAgICAgICBUaGUgY29uc3RyYWludCBpcyBjaGVja2VkIG9uIHF1ZXJ5IGV4ZWN1dGlvbiB0aW1lLlxuICAgICAgICBJZiBhIHF1ZXJ5IHRyaWVzIHRvIGFsbG9jYXRlIG1lbW9yeSBhbmQgdGhlIGN1cnJlbnQgbWVtb3J5IHVzYWdlIHBsdXMgYWxsb2NhdGlvbiBpcyBncmVhdGVyXG4gICAgICAgICAgdGhhbiBzcGVjaWZpZWQgdGhyZXNob2xkLCBleGNlcHRpb24gd2lsbCBiZSB0aHJvd24uXG5cbiAgICAgICAgSXQgaXMgbm90IHByYWN0aWNhbCB0byBzZXQgdGhpcyBjb25zdHJhaW50IHRvIHNtYWxsIHZhbHVlcyBsaWtlIGp1c3QgYSBmZXcgZ2lnYWJ5dGVzLFxuICAgICAgICAgIGJlY2F1c2UgbWVtb3J5IGFsbG9jYXRvciB3aWxsIGtlZXAgdGhpcyBhbW91bnQgb2YgbWVtb3J5IGluIGNhY2hlcyBhbmQgdGhlIHNlcnZlciB3aWxsIGRlbnkgc2VydmljZVxuICAgIG9mIHF1ZXJpZXMuXG4gICAgICAtLT5cbiAgICA8bWF4X3NlcnZlcl9tZW1vcnlfdXNhZ2U+MDwvbWF4X3NlcnZlcl9tZW1vcnlfdXNhZ2U+XG5cbiAgICA8IS0tIE1heGltdW0gbnVtYmVyIG9mIHRocmVhZHMgaW4gdGhlIEdsb2JhbCB0aHJlYWQgcG9vbC5cbiAgICBUaGlzIHdpbGwgZGVmYXVsdCB0byBhIG1heGltdW0gb2YgMTAwMDAgdGhyZWFkcyBpZiBub3Qgc3BlY2lmaWVkLlxuICAgIFRoaXMgc2V0dGluZyB3aWxsIGJlIHVzZWZ1bCBpbiBzY2VuYXJpb3Mgd2hlcmUgdGhlcmUgYXJlIGEgbGFyZ2UgbnVtYmVyXG4gICAgb2YgZGlzdHJpYnV0ZWQgcXVlcmllcyB0aGF0IGFyZSBydW5uaW5nIGNvbmN1cnJlbnRseSBidXQgYXJlIGlkbGluZyBtb3N0XG4gICAgb2YgdGhlIHRpbWUsIGluIHdoaWNoIGNhc2UgYSBoaWdoZXIgbnVtYmVyIG9mIHRocmVhZHMgbWlnaHQgYmUgcmVxdWlyZWQuXG4gICAgLS0+XG5cbiAgICA8bWF4X3RocmVhZF9wb29sX3NpemU+MTAwMDA8L21heF90aHJlYWRfcG9vbF9zaXplPlxuXG4gICAgPCEtLSBOdW1iZXIgb2Ygd29ya2VycyB0byByZWN5Y2xlIGNvbm5lY3Rpb25zIGluIGJhY2tncm91bmQgKHNlZSBhbHNvIGRyYWluX3RpbWVvdXQpLlxuICAgICAgICBJZiB0aGUgcG9vbCBpcyBmdWxsLCBjb25uZWN0aW9uIHdpbGwgYmUgZHJhaW5lZCBzeW5jaHJvbm91c2x5LiAtLT5cbiAgICA8IS0tIDxtYXhfdGhyZWFkc19mb3JfY29ubmVjdGlvbl9jb2xsZWN0b3I+MTA8L21heF90aHJlYWRzX2Zvcl9jb25uZWN0aW9uX2NvbGxlY3Rvcj4gLS0+XG5cbiAgICA8IS0tIE9uIG1lbW9yeSBjb25zdHJhaW5lZCBlbnZpcm9ubWVudHMgeW91IG1heSBoYXZlIHRvIHNldCB0aGlzIHRvIHZhbHVlIGxhcmdlciB0aGFuIDEuXG4gICAgICAtLT5cbiAgICA8bWF4X3NlcnZlcl9tZW1vcnlfdXNhZ2VfdG9fcmFtX3JhdGlvPjAuOTwvbWF4X3NlcnZlcl9tZW1vcnlfdXNhZ2VfdG9fcmFtX3JhdGlvPlxuXG4gICAgPCEtLSBTaW1wbGUgc2VydmVyLXdpZGUgbWVtb3J5IHByb2ZpbGVyLiBDb2xsZWN0IGEgc3RhY2sgdHJhY2UgYXQgZXZlcnkgcGVhayBhbGxvY2F0aW9uIHN0ZXAgKGluXG4gICAgYnl0ZXMpLlxuICAgICAgICBEYXRhIHdpbGwgYmUgc3RvcmVkIGluIHN5c3RlbS50cmFjZV9sb2cgdGFibGUgd2l0aCBxdWVyeV9pZCA9IGVtcHR5IHN0cmluZy5cbiAgICAgICAgWmVybyBtZWFucyBkaXNhYmxlZC5cbiAgICAgIC0tPlxuICAgIDx0b3RhbF9tZW1vcnlfcHJvZmlsZXJfc3RlcD40MTk0MzA0PC90b3RhbF9tZW1vcnlfcHJvZmlsZXJfc3RlcD5cblxuICAgIDwhLS0gQ29sbGVjdCByYW5kb20gYWxsb2NhdGlvbnMgYW5kIGRlYWxsb2NhdGlvbnMgYW5kIHdyaXRlIHRoZW0gaW50byBzeXN0ZW0udHJhY2VfbG9nIHdpdGhcbiAgICAnTWVtb3J5U2FtcGxlJyB0cmFjZV90eXBlLlxuICAgICAgICBUaGUgcHJvYmFiaWxpdHkgaXMgZm9yIGV2ZXJ5IGFsbG9jL2ZyZWUgcmVnYXJkbGVzcyB0byB0aGUgc2l6ZSBvZiB0aGUgYWxsb2NhdGlvbi5cbiAgICAgICAgTm90ZSB0aGF0IHNhbXBsaW5nIGhhcHBlbnMgb25seSB3aGVuIHRoZSBhbW91bnQgb2YgdW50cmFja2VkIG1lbW9yeSBleGNlZWRzIHRoZSB1bnRyYWNrZWQgbWVtb3J5XG4gICAgbGltaXQsXG4gICAgICAgICAgd2hpY2ggaXMgNCBNaUIgYnkgZGVmYXVsdCBidXQgY2FuIGJlIGxvd2VyZWQgaWYgJ3RvdGFsX21lbW9yeV9wcm9maWxlcl9zdGVwJyBpcyBsb3dlcmVkLlxuICAgICAgICBZb3UgbWF5IHdhbnQgdG8gc2V0ICd0b3RhbF9tZW1vcnlfcHJvZmlsZXJfc3RlcCcgdG8gMSBmb3IgZXh0cmEgZmluZSBncmFpbmVkIHNhbXBsaW5nLlxuICAgICAgLS0+XG4gICAgPHRvdGFsX21lbW9yeV90cmFja2VyX3NhbXBsZV9wcm9iYWJpbGl0eT4wPC90b3RhbF9tZW1vcnlfdHJhY2tlcl9zYW1wbGVfcHJvYmFiaWxpdHk+XG5cbiAgICA8IS0tIFNldCBsaW1pdCBvbiBudW1iZXIgb2Ygb3BlbiBmaWxlcyAoZGVmYXVsdDogbWF4aW11bSkuIFRoaXMgc2V0dGluZyBtYWtlcyBzZW5zZSBvbiBNYWMgT1MgWFxuICAgIGJlY2F1c2UgZ2V0cmxpbWl0KCkgZmFpbHMgdG8gcmV0cmlldmVcbiAgICAgICAgY29ycmVjdCBtYXhpbXVtIHZhbHVlLiAtLT5cbiAgICA8IS0tIDxtYXhfb3Blbl9maWxlcz4yNjIxNDQ8L21heF9vcGVuX2ZpbGVzPiAtLT5cblxuICAgIDwhLS0gU2l6ZSBvZiBjYWNoZSBvZiB1bmNvbXByZXNzZWQgYmxvY2tzIG9mIGRhdGEsIHVzZWQgaW4gdGFibGVzIG9mIE1lcmdlVHJlZSBmYW1pbHkuXG4gICAgICAgIEluIGJ5dGVzLiBDYWNoZSBpcyBzaW5nbGUgZm9yIHNlcnZlci4gTWVtb3J5IGlzIGFsbG9jYXRlZCBvbmx5IG9uIGRlbWFuZC5cbiAgICAgICAgQ2FjaGUgaXMgdXNlZCB3aGVuICd1c2VfdW5jb21wcmVzc2VkX2NhY2hlJyB1c2VyIHNldHRpbmcgdHVybmVkIG9uIChvZmYgYnkgZGVmYXVsdCkuXG4gICAgICAgIFVuY29tcHJlc3NlZCBjYWNoZSBpcyBhZHZhbnRhZ2VvdXMgb25seSBmb3IgdmVyeSBzaG9ydCBxdWVyaWVzIGFuZCBpbiByYXJlIGNhc2VzLlxuXG4gICAgICAgIE5vdGU6IHVuY29tcHJlc3NlZCBjYWNoZSBjYW4gYmUgcG9pbnRsZXNzIGZvciBsejQsIGJlY2F1c2UgbWVtb3J5IGJhbmR3aWR0aFxuICAgICAgICBpcyBzbG93ZXIgdGhhbiBtdWx0aS1jb3JlIGRlY29tcHJlc3Npb24gb24gc29tZSBzZXJ2ZXIgY29uZmlndXJhdGlvbnMuXG4gICAgICAgIEVuYWJsaW5nIGl0IGNhbiBzb21ldGltZXMgcGFyYWRveGljYWxseSBtYWtlIHF1ZXJpZXMgc2xvd2VyLlxuICAgICAgLS0+XG4gICAgPHVuY29tcHJlc3NlZF9jYWNoZV9zaXplPjg1ODk5MzQ1OTI8L3VuY29tcHJlc3NlZF9jYWNoZV9zaXplPlxuXG4gICAgPCEtLSBBcHByb3hpbWF0ZSBzaXplIG9mIG1hcmsgY2FjaGUsIHVzZWQgaW4gdGFibGVzIG9mIE1lcmdlVHJlZSBmYW1pbHkuXG4gICAgICAgIEluIGJ5dGVzLiBDYWNoZSBpcyBzaW5nbGUgZm9yIHNlcnZlci4gTWVtb3J5IGlzIGFsbG9jYXRlZCBvbmx5IG9uIGRlbWFuZC5cbiAgICAgICAgWW91IHNob3VsZCBub3QgbG93ZXIgdGhpcyB2YWx1ZS5cbiAgICAgIC0tPlxuICAgIDxtYXJrX2NhY2hlX3NpemU+NTM2ODcwOTEyMDwvbWFya19jYWNoZV9zaXplPlxuXG5cbiAgICA8IS0tIElmIHlvdSBlbmFibGUgdGhlIGBtaW5fYnl0ZXNfdG9fdXNlX21tYXBfaW9gIHNldHRpbmcsXG4gICAgICAgIHRoZSBkYXRhIGluIE1lcmdlVHJlZSB0YWJsZXMgY2FuIGJlIHJlYWQgd2l0aCBtbWFwIHRvIGF2b2lkIGNvcHlpbmcgZnJvbSBrZXJuZWwgdG8gdXNlcnNwYWNlLlxuICAgICAgICBJdCBtYWtlcyBzZW5zZSBvbmx5IGZvciBsYXJnZSBmaWxlcyBhbmQgaGVscHMgb25seSBpZiBkYXRhIHJlc2lkZSBpbiBwYWdlIGNhY2hlLlxuICAgICAgICBUbyBhdm9pZCBmcmVxdWVudCBvcGVuL21tYXAvbXVubWFwL2Nsb3NlIGNhbGxzICh3aGljaCBhcmUgdmVyeSBleHBlbnNpdmUgZHVlIHRvIGNvbnNlcXVlbnQgcGFnZVxuICAgIGZhdWx0cylcbiAgICAgICAgYW5kIHRvIHJldXNlIG1hcHBpbmdzIGZyb20gc2V2ZXJhbCB0aHJlYWRzIGFuZCBxdWVyaWVzLFxuICAgICAgICB0aGUgY2FjaGUgb2YgbWFwcGVkIGZpbGVzIGlzIG1haW50YWluZWQuIEl0cyBzaXplIGlzIHRoZSBudW1iZXIgb2YgbWFwcGVkIHJlZ2lvbnMgKHVzdWFsbHkgZXF1YWwgdG9cbiAgICB0aGUgbnVtYmVyIG9mIG1hcHBlZCBmaWxlcykuXG4gICAgICAgIFRoZSBhbW91bnQgb2YgZGF0YSBpbiBtYXBwZWQgZmlsZXMgY2FuIGJlIG1vbml0b3JlZFxuICAgICAgICBpbiBzeXN0ZW0ubWV0cmljcywgc3lzdGVtLm1ldHJpY19sb2cgYnkgdGhlIE1NYXBwZWRGaWxlcywgTU1hcHBlZEZpbGVCeXRlcyBtZXRyaWNzXG4gICAgICAgIGFuZCBpbiBzeXN0ZW0uYXN5bmNocm9ub3VzX21ldHJpY3MsIHN5c3RlbS5hc3luY2hyb25vdXNfbWV0cmljc19sb2cgYnkgdGhlIE1NYXBDYWNoZUNlbGxzIG1ldHJpYyxcbiAgICAgICAgYW5kIGFsc28gaW4gc3lzdGVtLmV2ZW50cywgc3lzdGVtLnByb2Nlc3Nlcywgc3lzdGVtLnF1ZXJ5X2xvZywgc3lzdGVtLnF1ZXJ5X3RocmVhZF9sb2csXG4gICAgc3lzdGVtLnF1ZXJ5X3ZpZXdzX2xvZyBieSB0aGVcbiAgICAgICAgQ3JlYXRlZFJlYWRCdWZmZXJNTWFwLCBDcmVhdGVkUmVhZEJ1ZmZlck1NYXBGYWlsZWQsIE1NYXBwZWRGaWxlQ2FjaGVIaXRzLCBNTWFwcGVkRmlsZUNhY2hlTWlzc2VzXG4gICAgZXZlbnRzLlxuICAgICAgICBOb3RlIHRoYXQgdGhlIGFtb3VudCBvZiBkYXRhIGluIG1hcHBlZCBmaWxlcyBkb2VzIG5vdCBjb25zdW1lIG1lbW9yeSBkaXJlY3RseSBhbmQgaXMgbm90IGFjY291bnRlZFxuICAgICAgICBpbiBxdWVyeSBvciBzZXJ2ZXIgbWVtb3J5IHVzYWdlIC0gYmVjYXVzZSB0aGlzIG1lbW9yeSBjYW4gYmUgZGlzY2FyZGVkIHNpbWlsYXIgdG8gT1MgcGFnZSBjYWNoZS5cbiAgICAgICAgVGhlIGNhY2hlIGlzIGRyb3BwZWQgKHRoZSBmaWxlcyBhcmUgY2xvc2VkKSBhdXRvbWF0aWNhbGx5IG9uIHJlbW92YWwgb2Ygb2xkIHBhcnRzIGluIE1lcmdlVHJlZSxcbiAgICAgICAgYWxzbyBpdCBjYW4gYmUgZHJvcHBlZCBtYW51YWxseSBieSB0aGUgU1lTVEVNIERST1AgTU1BUCBDQUNIRSBxdWVyeS5cbiAgICAgIC0tPlxuICAgIDxtbWFwX2NhY2hlX3NpemU+MTAwMDwvbW1hcF9jYWNoZV9zaXplPlxuXG4gICAgPCEtLSBDYWNoZSBzaXplIGluIGJ5dGVzIGZvciBjb21waWxlZCBleHByZXNzaW9ucy4tLT5cbiAgICA8Y29tcGlsZWRfZXhwcmVzc2lvbl9jYWNoZV9zaXplPjEzNDIxNzcyODwvY29tcGlsZWRfZXhwcmVzc2lvbl9jYWNoZV9zaXplPlxuXG4gICAgPCEtLSBDYWNoZSBzaXplIGluIGVsZW1lbnRzIGZvciBjb21waWxlZCBleHByZXNzaW9ucy4tLT5cbiAgICA8Y29tcGlsZWRfZXhwcmVzc2lvbl9jYWNoZV9lbGVtZW50c19zaXplPjEwMDAwPC9jb21waWxlZF9leHByZXNzaW9uX2NhY2hlX2VsZW1lbnRzX3NpemU+XG5cbiAgICA8IS0tIFBhdGggdG8gZGF0YSBkaXJlY3RvcnksIHdpdGggdHJhaWxpbmcgc2xhc2guIC0tPlxuICAgIDxwYXRoPi92YXIvbGliL2NsaWNraG91c2UvPC9wYXRoPlxuXG4gICAgPCEtLSBQYXRoIHRvIHRlbXBvcmFyeSBkYXRhIGZvciBwcm9jZXNzaW5nIGhhcmQgcXVlcmllcy4gLS0+XG4gICAgPHRtcF9wYXRoPi92YXIvbGliL2NsaWNraG91c2UvdG1wLzwvdG1wX3BhdGg+XG5cbiAgICA8IS0tIFBvbGljeSBmcm9tIHRoZSA8c3RvcmFnZV9jb25maWd1cmF0aW9uPiBmb3IgdGhlIHRlbXBvcmFyeSBmaWxlcy5cbiAgICAgICAgSWYgbm90IHNldCA8dG1wX3BhdGg+IGlzIHVzZWQsIG90aGVyd2lzZSA8dG1wX3BhdGg+IGlzIGlnbm9yZWQuXG5cbiAgICAgICAgTm90ZXM6XG4gICAgICAgIC0gbW92ZV9mYWN0b3IgICAgICAgICAgICAgIGlzIGlnbm9yZWRcbiAgICAgICAgLSBrZWVwX2ZyZWVfc3BhY2VfYnl0ZXMgICAgaXMgaWdub3JlZFxuICAgICAgICAtIG1heF9kYXRhX3BhcnRfc2l6ZV9ieXRlcyBpcyBpZ25vcmVkXG4gICAgICAgIC0geW91IG11c3QgaGF2ZSBleGFjdGx5IG9uZSB2b2x1bWUgaW4gdGhhdCBwb2xpY3lcbiAgICAtLT5cbiAgICA8IS0tIDx0bXBfcG9saWN5PnRtcDwvdG1wX3BvbGljeT4gLS0+XG5cbiAgICA8IS0tIERpcmVjdG9yeSB3aXRoIHVzZXIgcHJvdmlkZWQgZmlsZXMgdGhhdCBhcmUgYWNjZXNzaWJsZSBieSAnZmlsZScgdGFibGUgZnVuY3Rpb24uIC0tPlxuICAgIDx1c2VyX2ZpbGVzX3BhdGg+L3Zhci9saWIvY2xpY2tob3VzZS91c2VyX2ZpbGVzLzwvdXNlcl9maWxlc19wYXRoPlxuXG4gICAgPCEtLSBMREFQIHNlcnZlciBkZWZpbml0aW9ucy4gLS0+XG4gICAgPGxkYXBfc2VydmVycz5cbiAgICAgICAgPCEtLSBMaXN0IExEQVAgc2VydmVycyB3aXRoIHRoZWlyIGNvbm5lY3Rpb24gcGFyYW1ldGVycyBoZXJlIHRvIGxhdGVyIDEpIHVzZSB0aGVtIGFzXG4gICAgICAgIGF1dGhlbnRpY2F0b3JzIGZvciBkZWRpY2F0ZWQgbG9jYWwgdXNlcnMsXG4gICAgICAgICAgICAgIHdobyBoYXZlICdsZGFwJyBhdXRoZW50aWNhdGlvbiBtZWNoYW5pc20gc3BlY2lmaWVkIGluc3RlYWQgb2YgJ3Bhc3N3b3JkJywgb3IgdG8gMikgdXNlIHRoZW0gYXNcbiAgICAgICAgcmVtb3RlIHVzZXIgZGlyZWN0b3JpZXMuXG4gICAgICAgICAgICBQYXJhbWV0ZXJzOlxuICAgICAgICAgICAgICAgIGhvc3QgLSBMREFQIHNlcnZlciBob3N0bmFtZSBvciBJUCwgdGhpcyBwYXJhbWV0ZXIgaXMgbWFuZGF0b3J5IGFuZCBjYW5ub3QgYmUgZW1wdHkuXG4gICAgICAgICAgICAgICAgcG9ydCAtIExEQVAgc2VydmVyIHBvcnQsIGRlZmF1bHQgaXMgNjM2IGlmIGVuYWJsZV90bHMgaXMgc2V0IHRvIHRydWUsIDM4OSBvdGhlcndpc2UuXG4gICAgICAgICAgICAgICAgYmluZF9kbiAtIHRlbXBsYXRlIHVzZWQgdG8gY29uc3RydWN0IHRoZSBETiB0byBiaW5kIHRvLlxuICAgICAgICAgICAgICAgICAgICAgICAgVGhlIHJlc3VsdGluZyBETiB3aWxsIGJlIGNvbnN0cnVjdGVkIGJ5IHJlcGxhY2luZyBhbGwgJ3t1c2VyX25hbWV9JyBzdWJzdHJpbmdzIG9mIHRoZSB0ZW1wbGF0ZSB3aXRoXG4gICAgICAgIHRoZSBhY3R1YWxcbiAgICAgICAgICAgICAgICAgICAgICAgIHVzZXIgbmFtZSBkdXJpbmcgZWFjaCBhdXRoZW50aWNhdGlvbiBhdHRlbXB0LlxuICAgICAgICAgICAgICAgIHVzZXJfZG5fZGV0ZWN0aW9uIC0gc2VjdGlvbiB3aXRoIExEQVAgc2VhcmNoIHBhcmFtZXRlcnMgZm9yIGRldGVjdGluZyB0aGUgYWN0dWFsIHVzZXIgRE4gb2YgdGhlXG4gICAgICAgIGJvdW5kIHVzZXIuXG4gICAgICAgICAgICAgICAgICAgICAgICBUaGlzIGlzIG1haW5seSB1c2VkIGluIHNlYXJjaCBmaWx0ZXJzIGZvciBmdXJ0aGVyIHJvbGUgbWFwcGluZyB3aGVuIHRoZSBzZXJ2ZXIgaXMgQWN0aXZlIERpcmVjdG9yeS5cbiAgICAgICAgVGhlXG4gICAgICAgICAgICAgICAgICAgICAgICByZXN1bHRpbmcgdXNlciBETiB3aWxsIGJlIHVzZWQgd2hlbiByZXBsYWNpbmcgJ3t1c2VyX2RufScgc3Vic3RyaW5ncyB3aGVyZXZlciB0aGV5IGFyZSBhbGxvd2VkLiBCeVxuICAgICAgICBkZWZhdWx0LFxuICAgICAgICAgICAgICAgICAgICAgICAgdXNlciBETiBpcyBzZXQgZXF1YWwgdG8gYmluZCBETiwgYnV0IG9uY2Ugc2VhcmNoIGlzIHBlcmZvcm1lZCwgaXQgd2lsbCBiZSB1cGRhdGVkIHdpdGggdG8gdGhlXG4gICAgICAgIGFjdHVhbCBkZXRlY3RlZFxuICAgICAgICAgICAgICAgICAgICAgICAgdXNlciBETiB2YWx1ZS5cbiAgICAgICAgICAgICAgICAgICAgYmFzZV9kbiAtIHRlbXBsYXRlIHVzZWQgdG8gY29uc3RydWN0IHRoZSBiYXNlIEROIGZvciB0aGUgTERBUCBzZWFyY2guXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVGhlIHJlc3VsdGluZyBETiB3aWxsIGJlIGNvbnN0cnVjdGVkIGJ5IHJlcGxhY2luZyBhbGwgJ3t1c2VyX25hbWV9JyBhbmQgJ3tiaW5kX2RufScgc3Vic3RyaW5nc1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9mIHRoZSB0ZW1wbGF0ZSB3aXRoIHRoZSBhY3R1YWwgdXNlciBuYW1lIGFuZCBiaW5kIEROIGR1cmluZyB0aGUgTERBUCBzZWFyY2guXG4gICAgICAgICAgICAgICAgICAgIHNjb3BlIC0gc2NvcGUgb2YgdGhlIExEQVAgc2VhcmNoLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFjY2VwdGVkIHZhbHVlcyBhcmU6ICdiYXNlJywgJ29uZV9sZXZlbCcsICdjaGlsZHJlbicsICdzdWJ0cmVlJyAodGhlIGRlZmF1bHQpLlxuICAgICAgICAgICAgICAgICAgICBzZWFyY2hfZmlsdGVyIC0gdGVtcGxhdGUgdXNlZCB0byBjb25zdHJ1Y3QgdGhlIHNlYXJjaCBmaWx0ZXIgZm9yIHRoZSBMREFQIHNlYXJjaC5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUaGUgcmVzdWx0aW5nIGZpbHRlciB3aWxsIGJlIGNvbnN0cnVjdGVkIGJ5IHJlcGxhY2luZyBhbGwgJ3t1c2VyX25hbWV9JywgJ3tiaW5kX2RufScsIGFuZFxuICAgICAgICAne2Jhc2VfZG59J1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnN0cmluZ3Mgb2YgdGhlIHRlbXBsYXRlIHdpdGggdGhlIGFjdHVhbCB1c2VyIG5hbWUsIGJpbmQgRE4sIGFuZCBiYXNlIEROIGR1cmluZyB0aGUgTERBUCBzZWFyY2guXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgTm90ZSwgdGhhdCB0aGUgc3BlY2lhbCBjaGFyYWN0ZXJzIG11c3QgYmUgZXNjYXBlZCBwcm9wZXJseSBpbiBYTUwuXG4gICAgICAgICAgICAgICAgdmVyaWZpY2F0aW9uX2Nvb2xkb3duIC0gYSBwZXJpb2Qgb2YgdGltZSwgaW4gc2Vjb25kcywgYWZ0ZXIgYSBzdWNjZXNzZnVsIGJpbmQgYXR0ZW1wdCwgZHVyaW5nIHdoaWNoXG4gICAgICAgIGEgdXNlciB3aWxsIGJlIGFzc3VtZWRcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvIGJlIHN1Y2Nlc3NmdWxseSBhdXRoZW50aWNhdGVkIGZvciBhbGwgY29uc2VjdXRpdmUgcmVxdWVzdHMgd2l0aG91dCBjb250YWN0aW5nIHRoZSBMREFQIHNlcnZlci5cbiAgICAgICAgICAgICAgICAgICAgICAgIFNwZWNpZnkgMCAodGhlIGRlZmF1bHQpIHRvIGRpc2FibGUgY2FjaGluZyBhbmQgZm9yY2UgY29udGFjdGluZyB0aGUgTERBUCBzZXJ2ZXIgZm9yIGVhY2hcbiAgICAgICAgYXV0aGVudGljYXRpb24gcmVxdWVzdC5cbiAgICAgICAgICAgICAgICBlbmFibGVfdGxzIC0gZmxhZyB0byB0cmlnZ2VyIHVzZSBvZiBzZWN1cmUgY29ubmVjdGlvbiB0byB0aGUgTERBUCBzZXJ2ZXIuXG4gICAgICAgICAgICAgICAgICAgICAgICBTcGVjaWZ5ICdubycgZm9yIHBsYWluIHRleHQgKGxkYXA6Ly8pIHByb3RvY29sIChub3QgcmVjb21tZW5kZWQpLlxuICAgICAgICAgICAgICAgICAgICAgICAgU3BlY2lmeSAneWVzJyBmb3IgTERBUCBvdmVyIFNTTC9UTFMgKGxkYXBzOi8vKSBwcm90b2NvbCAocmVjb21tZW5kZWQsIHRoZSBkZWZhdWx0KS5cbiAgICAgICAgICAgICAgICAgICAgICAgIFNwZWNpZnkgJ3N0YXJ0dGxzJyBmb3IgbGVnYWN5IFN0YXJ0VExTIHByb3RvY29sIChwbGFpbiB0ZXh0IChsZGFwOi8vKSBwcm90b2NvbCwgdXBncmFkZWQgdG8gVExTKS5cbiAgICAgICAgICAgICAgICB0bHNfbWluaW11bV9wcm90b2NvbF92ZXJzaW9uIC0gdGhlIG1pbmltdW0gcHJvdG9jb2wgdmVyc2lvbiBvZiBTU0wvVExTLlxuICAgICAgICAgICAgICAgICAgICAgICAgQWNjZXB0ZWQgdmFsdWVzIGFyZTogJ3NzbDInLCAnc3NsMycsICd0bHMxLjAnLCAndGxzMS4xJywgJ3RsczEuMicgKHRoZSBkZWZhdWx0KS5cbiAgICAgICAgICAgICAgICB0bHNfcmVxdWlyZV9jZXJ0IC0gU1NML1RMUyBwZWVyIGNlcnRpZmljYXRlIHZlcmlmaWNhdGlvbiBiZWhhdmlvci5cbiAgICAgICAgICAgICAgICAgICAgICAgIEFjY2VwdGVkIHZhbHVlcyBhcmU6ICduZXZlcicsICdhbGxvdycsICd0cnknLCAnZGVtYW5kJyAodGhlIGRlZmF1bHQpLlxuICAgICAgICAgICAgICAgIHRsc19jZXJ0X2ZpbGUgLSBwYXRoIHRvIGNlcnRpZmljYXRlIGZpbGUuXG4gICAgICAgICAgICAgICAgdGxzX2tleV9maWxlIC0gcGF0aCB0byBjZXJ0aWZpY2F0ZSBrZXkgZmlsZS5cbiAgICAgICAgICAgICAgICB0bHNfY2FfY2VydF9maWxlIC0gcGF0aCB0byBDQSBjZXJ0aWZpY2F0ZSBmaWxlLlxuICAgICAgICAgICAgICAgIHRsc19jYV9jZXJ0X2RpciAtIHBhdGggdG8gdGhlIGRpcmVjdG9yeSBjb250YWluaW5nIENBIGNlcnRpZmljYXRlcy5cbiAgICAgICAgICAgICAgICB0bHNfY2lwaGVyX3N1aXRlIC0gYWxsb3dlZCBjaXBoZXIgc3VpdGUgKGluIE9wZW5TU0wgbm90YXRpb24pLlxuICAgICAgICAgICAgRXhhbXBsZTpcbiAgICAgICAgICAgICAgICA8bXlfbGRhcF9zZXJ2ZXI+XG4gICAgICAgICAgICAgICAgICAgIDxob3N0PmxvY2FsaG9zdDwvaG9zdD5cbiAgICAgICAgICAgICAgICAgICAgPHBvcnQ+NjM2PC9wb3J0PlxuICAgICAgICAgICAgICAgICAgICA8YmluZF9kbj51aWQ9e3VzZXJfbmFtZX0sb3U9dXNlcnMsZGM9ZXhhbXBsZSxkYz1jb208L2JpbmRfZG4+XG4gICAgICAgICAgICAgICAgICAgIDx2ZXJpZmljYXRpb25fY29vbGRvd24+MzAwPC92ZXJpZmljYXRpb25fY29vbGRvd24+XG4gICAgICAgICAgICAgICAgICAgIDxlbmFibGVfdGxzPnllczwvZW5hYmxlX3Rscz5cbiAgICAgICAgICAgICAgICAgICAgPHRsc19taW5pbXVtX3Byb3RvY29sX3ZlcnNpb24+dGxzMS4yPC90bHNfbWluaW11bV9wcm90b2NvbF92ZXJzaW9uPlxuICAgICAgICAgICAgICAgICAgICA8dGxzX3JlcXVpcmVfY2VydD5kZW1hbmQ8L3Rsc19yZXF1aXJlX2NlcnQ+XG4gICAgICAgICAgICAgICAgICAgIDx0bHNfY2VydF9maWxlPi9wYXRoL3RvL3Rsc19jZXJ0X2ZpbGU8L3Rsc19jZXJ0X2ZpbGU+XG4gICAgICAgICAgICAgICAgICAgIDx0bHNfa2V5X2ZpbGU+L3BhdGgvdG8vdGxzX2tleV9maWxlPC90bHNfa2V5X2ZpbGU+XG4gICAgICAgICAgICAgICAgICAgIDx0bHNfY2FfY2VydF9maWxlPi9wYXRoL3RvL3Rsc19jYV9jZXJ0X2ZpbGU8L3Rsc19jYV9jZXJ0X2ZpbGU+XG4gICAgICAgICAgICAgICAgICAgIDx0bHNfY2FfY2VydF9kaXI+L3BhdGgvdG8vdGxzX2NhX2NlcnRfZGlyPC90bHNfY2FfY2VydF9kaXI+XG4gICAgICAgIDx0bHNfY2lwaGVyX3N1aXRlPkVDREhFLUVDRFNBLUFFUzI1Ni1HQ00tU0hBMzg0OkVDREhFLVJTQS1BRVMyNTYtR0NNLVNIQTM4NDpBRVMyNTYtR0NNLVNIQTM4NDwvdGxzX2NpcGhlcl9zdWl0ZT5cbiAgICAgICAgICAgICAgICA8L215X2xkYXBfc2VydmVyPlxuICAgICAgICAgICAgRXhhbXBsZSAodHlwaWNhbCBBY3RpdmUgRGlyZWN0b3J5IHdpdGggY29uZmlndXJlZCB1c2VyIEROIGRldGVjdGlvbiBmb3IgZnVydGhlciByb2xlIG1hcHBpbmcpOlxuICAgICAgICAgICAgICAgIDxteV9hZF9zZXJ2ZXI+XG4gICAgICAgICAgICAgICAgICAgIDxob3N0PmxvY2FsaG9zdDwvaG9zdD5cbiAgICAgICAgICAgICAgICAgICAgPHBvcnQ+Mzg5PC9wb3J0PlxuICAgICAgICAgICAgICAgICAgICA8YmluZF9kbj5FWEFNUExFXFx7dXNlcl9uYW1lfTwvYmluZF9kbj5cbiAgICAgICAgICAgICAgICAgICAgPHVzZXJfZG5fZGV0ZWN0aW9uPlxuICAgICAgICAgICAgICAgICAgICAgICAgPGJhc2VfZG4+Q049VXNlcnMsREM9ZXhhbXBsZSxEQz1jb208L2Jhc2VfZG4+XG4gICAgICAgICAgICAgICAgICAgICAgICA8c2VhcmNoX2ZpbHRlcj4oJmFtcDsob2JqZWN0Q2xhc3M9dXNlcikoc0FNQWNjb3VudE5hbWU9e3VzZXJfbmFtZX0pKTwvc2VhcmNoX2ZpbHRlcj5cbiAgICAgICAgICAgICAgICAgICAgPC91c2VyX2RuX2RldGVjdGlvbj5cbiAgICAgICAgICAgICAgICAgICAgPGVuYWJsZV90bHM+bm88L2VuYWJsZV90bHM+XG4gICAgICAgICAgICAgICAgPC9teV9hZF9zZXJ2ZXI+XG4gICAgICAgIC0tPlxuICAgIDwvbGRhcF9zZXJ2ZXJzPlxuXG4gICAgPCEtLSBUbyBlbmFibGUgS2VyYmVyb3MgYXV0aGVudGljYXRpb24gc3VwcG9ydCBmb3IgSFRUUCByZXF1ZXN0cyAoR1NTLVNQTkVHTyksIGZvciB0aG9zZSB1c2Vyc1xuICAgIHdobyBhcmUgZXhwbGljaXRseSBjb25maWd1cmVkXG4gICAgICAgICAgdG8gYXV0aGVudGljYXRlIHZpYSBLZXJiZXJvcywgZGVmaW5lIGEgc2luZ2xlICdrZXJiZXJvcycgc2VjdGlvbiBoZXJlLlxuICAgICAgICBQYXJhbWV0ZXJzOlxuICAgICAgICAgICAgcHJpbmNpcGFsIC0gY2Fub25pY2FsIHNlcnZpY2UgcHJpbmNpcGFsIG5hbWUsIHRoYXQgd2lsbCBiZSBhY3F1aXJlZCBhbmQgdXNlZCB3aGVuIGFjY2VwdGluZ1xuICAgIHNlY3VyaXR5IGNvbnRleHRzLlxuICAgICAgICAgICAgICAgICAgICBUaGlzIHBhcmFtZXRlciBpcyBvcHRpb25hbCwgaWYgb21pdHRlZCwgdGhlIGRlZmF1bHQgcHJpbmNpcGFsIHdpbGwgYmUgdXNlZC5cbiAgICAgICAgICAgICAgICAgICAgVGhpcyBwYXJhbWV0ZXIgY2Fubm90IGJlIHNwZWNpZmllZCB0b2dldGhlciB3aXRoICdyZWFsbScgcGFyYW1ldGVyLlxuICAgICAgICAgICAgcmVhbG0gLSBhIHJlYWxtLCB0aGF0IHdpbGwgYmUgdXNlZCB0byByZXN0cmljdCBhdXRoZW50aWNhdGlvbiB0byBvbmx5IHRob3NlIHJlcXVlc3RzIHdob3NlXG4gICAgaW5pdGlhdG9yJ3MgcmVhbG0gbWF0Y2hlcyBpdC5cbiAgICAgICAgICAgICAgICAgICAgVGhpcyBwYXJhbWV0ZXIgaXMgb3B0aW9uYWwsIGlmIG9taXR0ZWQsIG5vIGFkZGl0aW9uYWwgZmlsdGVyaW5nIGJ5IHJlYWxtIHdpbGwgYmUgYXBwbGllZC5cbiAgICAgICAgICAgICAgICAgICAgVGhpcyBwYXJhbWV0ZXIgY2Fubm90IGJlIHNwZWNpZmllZCB0b2dldGhlciB3aXRoICdwcmluY2lwYWwnIHBhcmFtZXRlci5cbiAgICAgICAgRXhhbXBsZTpcbiAgICAgICAgICAgIDxrZXJiZXJvcyAvPlxuICAgICAgICBFeGFtcGxlOlxuICAgICAgICAgICAgPGtlcmJlcm9zPlxuICAgICAgICAgICAgICAgIDxwcmluY2lwYWw+SFRUUC9jbGlja2hvdXNlLmV4YW1wbGUuY29tQEVYQU1QTEUuQ09NPC9wcmluY2lwYWw+XG4gICAgICAgICAgICA8L2tlcmJlcm9zPlxuICAgICAgICBFeGFtcGxlOlxuICAgICAgICAgICAgPGtlcmJlcm9zPlxuICAgICAgICAgICAgICAgIDxyZWFsbT5FWEFNUExFLkNPTTwvcmVhbG0+XG4gICAgICAgICAgICA8L2tlcmJlcm9zPlxuICAgIC0tPlxuXG4gICAgPCEtLSBTb3VyY2VzIHRvIHJlYWQgdXNlcnMsIHJvbGVzLCBhY2Nlc3MgcmlnaHRzLCBwcm9maWxlcyBvZiBzZXR0aW5ncywgcXVvdGFzLiAtLT5cbiAgICA8dXNlcl9kaXJlY3Rvcmllcz5cbiAgICAgICAgPHVzZXJzX3htbD5cbiAgICAgICAgICAgIDwhLS0gUGF0aCB0byBjb25maWd1cmF0aW9uIGZpbGUgd2l0aCBwcmVkZWZpbmVkIHVzZXJzLiAtLT5cbiAgICAgICAgICAgIDxwYXRoPnVzZXJzLnhtbDwvcGF0aD5cbiAgICAgICAgPC91c2Vyc194bWw+XG4gICAgICAgIDxsb2NhbF9kaXJlY3Rvcnk+XG4gICAgICAgICAgICA8IS0tIFBhdGggdG8gZm9sZGVyIHdoZXJlIHVzZXJzIGNyZWF0ZWQgYnkgU1FMIGNvbW1hbmRzIGFyZSBzdG9yZWQuIC0tPlxuICAgICAgICAgICAgPHBhdGg+L3Zhci9saWIvY2xpY2tob3VzZS9hY2Nlc3MvPC9wYXRoPlxuICAgICAgICA8L2xvY2FsX2RpcmVjdG9yeT5cblxuICAgICAgICA8IS0tIFRvIGFkZCBhbiBMREFQIHNlcnZlciBhcyBhIHJlbW90ZSB1c2VyIGRpcmVjdG9yeSBvZiB1c2VycyB0aGF0IGFyZSBub3QgZGVmaW5lZCBsb2NhbGx5LFxuICAgICAgICBkZWZpbmUgYSBzaW5nbGUgJ2xkYXAnIHNlY3Rpb25cbiAgICAgICAgICAgICAgd2l0aCB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnM6XG4gICAgICAgICAgICAgICAgc2VydmVyIC0gb25lIG9mIExEQVAgc2VydmVyIG5hbWVzIGRlZmluZWQgaW4gJ2xkYXBfc2VydmVycycgY29uZmlnIHNlY3Rpb24gYWJvdmUuXG4gICAgICAgICAgICAgICAgICAgICAgICBUaGlzIHBhcmFtZXRlciBpcyBtYW5kYXRvcnkgYW5kIGNhbm5vdCBiZSBlbXB0eS5cbiAgICAgICAgICAgICAgICByb2xlcyAtIHNlY3Rpb24gd2l0aCBhIGxpc3Qgb2YgbG9jYWxseSBkZWZpbmVkIHJvbGVzIHRoYXQgd2lsbCBiZSBhc3NpZ25lZCB0byBlYWNoIHVzZXIgcmV0cmlldmVkXG4gICAgICAgIGZyb20gdGhlIExEQVAgc2VydmVyLlxuICAgICAgICAgICAgICAgICAgICAgICAgSWYgbm8gcm9sZXMgYXJlIHNwZWNpZmllZCBoZXJlIG9yIGFzc2lnbmVkIGR1cmluZyByb2xlIG1hcHBpbmcgKGJlbG93KSwgdXNlciB3aWxsIG5vdCBiZSBhYmxlIHRvXG4gICAgICAgIHBlcmZvcm0gYW55XG4gICAgICAgICAgICAgICAgICAgICAgICBhY3Rpb25zIGFmdGVyIGF1dGhlbnRpY2F0aW9uLlxuICAgICAgICAgICAgICAgIHJvbGVfbWFwcGluZyAtIHNlY3Rpb24gd2l0aCBMREFQIHNlYXJjaCBwYXJhbWV0ZXJzIGFuZCBtYXBwaW5nIHJ1bGVzLlxuICAgICAgICAgICAgICAgICAgICAgICAgV2hlbiBhIHVzZXIgYXV0aGVudGljYXRlcywgd2hpbGUgc3RpbGwgYm91bmQgdG8gTERBUCwgYW4gTERBUCBzZWFyY2ggaXMgcGVyZm9ybWVkIHVzaW5nXG4gICAgICAgIHNlYXJjaF9maWx0ZXIgYW5kIHRoZVxuICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSBvZiB0aGUgbG9nZ2VkIGluIHVzZXIuIEZvciBlYWNoIGVudHJ5IGZvdW5kIGR1cmluZyB0aGF0IHNlYXJjaCwgdGhlIHZhbHVlIG9mIHRoZSBzcGVjaWZpZWRcbiAgICAgICAgYXR0cmlidXRlIGlzXG4gICAgICAgICAgICAgICAgICAgICAgICBleHRyYWN0ZWQuIEZvciBlYWNoIGF0dHJpYnV0ZSB2YWx1ZSB0aGF0IGhhcyB0aGUgc3BlY2lmaWVkIHByZWZpeCwgdGhlIHByZWZpeCBpcyByZW1vdmVkLCBhbmQgdGhlXG4gICAgICAgIHJlc3Qgb2YgdGhlXG4gICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSBiZWNvbWVzIHRoZSBuYW1lIG9mIGEgbG9jYWwgcm9sZSBkZWZpbmVkIGluIENsaWNrSG91c2UsIHdoaWNoIGlzIGV4cGVjdGVkIHRvIGJlIGNyZWF0ZWRcbiAgICAgICAgYmVmb3JlaGFuZCBieVxuICAgICAgICAgICAgICAgICAgICAgICAgQ1JFQVRFIFJPTEUgY29tbWFuZC5cbiAgICAgICAgICAgICAgICAgICAgICAgIFRoZXJlIGNhbiBiZSBtdWx0aXBsZSAncm9sZV9tYXBwaW5nJyBzZWN0aW9ucyBkZWZpbmVkIGluc2lkZSB0aGUgc2FtZSAnbGRhcCcgc2VjdGlvbi4gQWxsIG9mIHRoZW1cbiAgICAgICAgd2lsbCBiZVxuICAgICAgICAgICAgICAgICAgICAgICAgYXBwbGllZC5cbiAgICAgICAgICAgICAgICAgICAgYmFzZV9kbiAtIHRlbXBsYXRlIHVzZWQgdG8gY29uc3RydWN0IHRoZSBiYXNlIEROIGZvciB0aGUgTERBUCBzZWFyY2guXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVGhlIHJlc3VsdGluZyBETiB3aWxsIGJlIGNvbnN0cnVjdGVkIGJ5IHJlcGxhY2luZyBhbGwgJ3t1c2VyX25hbWV9JywgJ3tiaW5kX2RufScsIGFuZCAne3VzZXJfZG59J1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnN0cmluZ3Mgb2YgdGhlIHRlbXBsYXRlIHdpdGggdGhlIGFjdHVhbCB1c2VyIG5hbWUsIGJpbmQgRE4sIGFuZCB1c2VyIEROIGR1cmluZyBlYWNoIExEQVAgc2VhcmNoLlxuICAgICAgICAgICAgICAgICAgICBzY29wZSAtIHNjb3BlIG9mIHRoZSBMREFQIHNlYXJjaC5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBBY2NlcHRlZCB2YWx1ZXMgYXJlOiAnYmFzZScsICdvbmVfbGV2ZWwnLCAnY2hpbGRyZW4nLCAnc3VidHJlZScgKHRoZSBkZWZhdWx0KS5cbiAgICAgICAgICAgICAgICAgICAgc2VhcmNoX2ZpbHRlciAtIHRlbXBsYXRlIHVzZWQgdG8gY29uc3RydWN0IHRoZSBzZWFyY2ggZmlsdGVyIGZvciB0aGUgTERBUCBzZWFyY2guXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVGhlIHJlc3VsdGluZyBmaWx0ZXIgd2lsbCBiZSBjb25zdHJ1Y3RlZCBieSByZXBsYWNpbmcgYWxsICd7dXNlcl9uYW1lfScsICd7YmluZF9kbn0nLCAne3VzZXJfZG59JyxcbiAgICAgICAgYW5kXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3tiYXNlX2RufScgc3Vic3RyaW5ncyBvZiB0aGUgdGVtcGxhdGUgd2l0aCB0aGUgYWN0dWFsIHVzZXIgbmFtZSwgYmluZCBETiwgdXNlciBETiwgYW5kIGJhc2UgRE5cbiAgICAgICAgZHVyaW5nXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZWFjaCBMREFQIHNlYXJjaC5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBOb3RlLCB0aGF0IHRoZSBzcGVjaWFsIGNoYXJhY3RlcnMgbXVzdCBiZSBlc2NhcGVkIHByb3Blcmx5IGluIFhNTC5cbiAgICAgICAgICAgICAgICAgICAgYXR0cmlidXRlIC0gYXR0cmlidXRlIG5hbWUgd2hvc2UgdmFsdWVzIHdpbGwgYmUgcmV0dXJuZWQgYnkgdGhlIExEQVAgc2VhcmNoLiAnY24nLCBieSBkZWZhdWx0LlxuICAgICAgICAgICAgICAgICAgICBwcmVmaXggLSBwcmVmaXgsIHRoYXQgd2lsbCBiZSBleHBlY3RlZCB0byBiZSBpbiBmcm9udCBvZiBlYWNoIHN0cmluZyBpbiB0aGUgb3JpZ2luYWwgbGlzdCBvZlxuICAgICAgICBzdHJpbmdzIHJldHVybmVkIGJ5XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlIExEQVAgc2VhcmNoLiBQcmVmaXggd2lsbCBiZSByZW1vdmVkIGZyb20gdGhlIG9yaWdpbmFsIHN0cmluZ3MgYW5kIHJlc3VsdGluZyBzdHJpbmdzIHdpbGwgYmVcbiAgICAgICAgdHJlYXRlZFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzIGxvY2FsIHJvbGUgbmFtZXMuIEVtcHR5LCBieSBkZWZhdWx0LlxuICAgICAgICAgICAgRXhhbXBsZTpcbiAgICAgICAgICAgICAgICA8bGRhcD5cbiAgICAgICAgICAgICAgICAgICAgPHNlcnZlcj5teV9sZGFwX3NlcnZlcjwvc2VydmVyPlxuICAgICAgICAgICAgICAgICAgICA8cm9sZXM+XG4gICAgICAgICAgICAgICAgICAgICAgICA8bXlfbG9jYWxfcm9sZTEgLz5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxteV9sb2NhbF9yb2xlMiAvPlxuICAgICAgICAgICAgICAgICAgICA8L3JvbGVzPlxuICAgICAgICAgICAgICAgICAgICA8cm9sZV9tYXBwaW5nPlxuICAgICAgICAgICAgICAgICAgICAgICAgPGJhc2VfZG4+b3U9Z3JvdXBzLGRjPWV4YW1wbGUsZGM9Y29tPC9iYXNlX2RuPlxuICAgICAgICAgICAgICAgICAgICAgICAgPHNjb3BlPnN1YnRyZWU8L3Njb3BlPlxuICAgICAgICAgICAgICAgICAgICAgICAgPHNlYXJjaF9maWx0ZXI+KCZhbXA7KG9iamVjdENsYXNzPWdyb3VwT2ZOYW1lcykobWVtYmVyPXtiaW5kX2RufSkpPC9zZWFyY2hfZmlsdGVyPlxuICAgICAgICAgICAgICAgICAgICAgICAgPGF0dHJpYnV0ZT5jbjwvYXR0cmlidXRlPlxuICAgICAgICAgICAgICAgICAgICAgICAgPHByZWZpeD5jbGlja2hvdXNlXzwvcHJlZml4PlxuICAgICAgICAgICAgICAgICAgICA8L3JvbGVfbWFwcGluZz5cbiAgICAgICAgICAgICAgICA8L2xkYXA+XG4gICAgICAgICAgICBFeGFtcGxlICh0eXBpY2FsIEFjdGl2ZSBEaXJlY3Rvcnkgd2l0aCByb2xlIG1hcHBpbmcgdGhhdCByZWxpZXMgb24gdGhlIGRldGVjdGVkIHVzZXIgRE4pOlxuICAgICAgICAgICAgICAgIDxsZGFwPlxuICAgICAgICAgICAgICAgICAgICA8c2VydmVyPm15X2FkX3NlcnZlcjwvc2VydmVyPlxuICAgICAgICAgICAgICAgICAgICA8cm9sZV9tYXBwaW5nPlxuICAgICAgICAgICAgICAgICAgICAgICAgPGJhc2VfZG4+Q049VXNlcnMsREM9ZXhhbXBsZSxEQz1jb208L2Jhc2VfZG4+XG4gICAgICAgICAgICAgICAgICAgICAgICA8YXR0cmlidXRlPkNOPC9hdHRyaWJ1dGU+XG4gICAgICAgICAgICAgICAgICAgICAgICA8c2NvcGU+c3VidHJlZTwvc2NvcGU+XG4gICAgICAgICAgICAgICAgICAgICAgICA8c2VhcmNoX2ZpbHRlcj4oJmFtcDsob2JqZWN0Q2xhc3M9Z3JvdXApKG1lbWJlcj17dXNlcl9kbn0pKTwvc2VhcmNoX2ZpbHRlcj5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxwcmVmaXg+Y2xpY2tob3VzZV88L3ByZWZpeD5cbiAgICAgICAgICAgICAgICAgICAgPC9yb2xlX21hcHBpbmc+XG4gICAgICAgICAgICAgICAgPC9sZGFwPlxuICAgICAgICAtLT5cbiAgICA8L3VzZXJfZGlyZWN0b3JpZXM+XG5cbiAgICA8IS0tIERlZmF1bHQgcHJvZmlsZSBvZiBzZXR0aW5ncy4gLS0+XG4gICAgPGRlZmF1bHRfcHJvZmlsZT5kZWZhdWx0PC9kZWZhdWx0X3Byb2ZpbGU+XG5cbiAgICA8IS0tIENvbW1hLXNlcGFyYXRlZCBsaXN0IG9mIHByZWZpeGVzIGZvciB1c2VyLWRlZmluZWQgc2V0dGluZ3MuIC0tPlxuICAgIDxjdXN0b21fc2V0dGluZ3NfcHJlZml4ZXM+PC9jdXN0b21fc2V0dGluZ3NfcHJlZml4ZXM+XG5cbiAgICA8IS0tIFN5c3RlbSBwcm9maWxlIG9mIHNldHRpbmdzLiBUaGlzIHNldHRpbmdzIGFyZSB1c2VkIGJ5IGludGVybmFsIHByb2Nlc3NlcyAoRGlzdHJpYnV0ZWQgRERMXG4gICAgd29ya2VyIGFuZCBzbyBvbikuIC0tPlxuICAgIDwhLS0gPHN5c3RlbV9wcm9maWxlPmRlZmF1bHQ8L3N5c3RlbV9wcm9maWxlPiAtLT5cblxuICAgIDwhLS0gQnVmZmVyIHByb2ZpbGUgb2Ygc2V0dGluZ3MuXG4gICAgICAgIFRoaXMgc2V0dGluZ3MgYXJlIHVzZWQgYnkgQnVmZmVyIHN0b3JhZ2UgdG8gZmx1c2ggZGF0YSB0byB0aGUgdW5kZXJseWluZyB0YWJsZS5cbiAgICAgICAgRGVmYXVsdDogdXNlZCBmcm9tIHN5c3RlbV9wcm9maWxlIGRpcmVjdGl2ZS5cbiAgICAtLT5cbiAgICA8IS0tIDxidWZmZXJfcHJvZmlsZT5kZWZhdWx0PC9idWZmZXJfcHJvZmlsZT4gLS0+XG5cbiAgICA8IS0tIERlZmF1bHQgZGF0YWJhc2UuIC0tPlxuICAgIDxkZWZhdWx0X2RhdGFiYXNlPmRlZmF1bHQ8L2RlZmF1bHRfZGF0YWJhc2U+XG5cbiAgICA8IS0tIFNlcnZlciB0aW1lIHpvbmUgY291bGQgYmUgc2V0IGhlcmUuXG5cbiAgICAgICAgVGltZSB6b25lIGlzIHVzZWQgd2hlbiBjb252ZXJ0aW5nIGJldHdlZW4gU3RyaW5nIGFuZCBEYXRlVGltZSB0eXBlcyxcbiAgICAgICAgICB3aGVuIHByaW50aW5nIERhdGVUaW1lIGluIHRleHQgZm9ybWF0cyBhbmQgcGFyc2luZyBEYXRlVGltZSBmcm9tIHRleHQsXG4gICAgICAgICAgaXQgaXMgdXNlZCBpbiBkYXRlIGFuZCB0aW1lIHJlbGF0ZWQgZnVuY3Rpb25zLCBpZiBzcGVjaWZpYyB0aW1lIHpvbmUgd2FzIG5vdCBwYXNzZWQgYXMgYW4gYXJndW1lbnQuXG5cbiAgICAgICAgVGltZSB6b25lIGlzIHNwZWNpZmllZCBhcyBpZGVudGlmaWVyIGZyb20gSUFOQSB0aW1lIHpvbmUgZGF0YWJhc2UsIGxpa2UgVVRDIG9yIEFmcmljYS9BYmlkamFuLlxuICAgICAgICBJZiBub3Qgc3BlY2lmaWVkLCBzeXN0ZW0gdGltZSB6b25lIGF0IHNlcnZlciBzdGFydHVwIGlzIHVzZWQuXG5cbiAgICAgICAgUGxlYXNlIG5vdGUsIHRoYXQgc2VydmVyIGNvdWxkIGRpc3BsYXkgdGltZSB6b25lIGFsaWFzIGluc3RlYWQgb2Ygc3BlY2lmaWVkIG5hbWUuXG4gICAgICAgIEV4YW1wbGU6IFctU1UgaXMgYW4gYWxpYXMgZm9yIEV1cm9wZS9Nb3Njb3cgYW5kIFp1bHUgaXMgYW4gYWxpYXMgZm9yIFVUQy5cbiAgICAtLT5cbiAgICA8IS0tIDx0aW1lem9uZT5FdXJvcGUvTW9zY293PC90aW1lem9uZT4gLS0+XG5cbiAgICA8IS0tIFlvdSBjYW4gc3BlY2lmeSB1bWFzayBoZXJlIChzZWUgXCJtYW4gdW1hc2tcIikuIFNlcnZlciB3aWxsIGFwcGx5IGl0IG9uIHN0YXJ0dXAuXG4gICAgICAgIE51bWJlciBpcyBhbHdheXMgcGFyc2VkIGFzIG9jdGFsLiBEZWZhdWx0IHVtYXNrIGlzIDAyNyAob3RoZXIgdXNlcnMgY2Fubm90IHJlYWQgbG9ncywgZGF0YSBmaWxlcyxcbiAgICBldGM7IGdyb3VwIGNhbiBvbmx5IHJlYWQpLlxuICAgIC0tPlxuICAgIDwhLS0gPHVtYXNrPjAyMjwvdW1hc2s+IC0tPlxuXG4gICAgPCEtLSBQZXJmb3JtIG1sb2NrYWxsIGFmdGVyIHN0YXJ0dXAgdG8gbG93ZXIgZmlyc3QgcXVlcmllcyBsYXRlbmN5XG4gICAgICAgICAgYW5kIHRvIHByZXZlbnQgY2xpY2tob3VzZSBleGVjdXRhYmxlIGZyb20gYmVpbmcgcGFnZWQgb3V0IHVuZGVyIGhpZ2ggSU8gbG9hZC5cbiAgICAgICAgRW5hYmxpbmcgdGhpcyBvcHRpb24gaXMgcmVjb21tZW5kZWQgYnV0IHdpbGwgbGVhZCB0byBpbmNyZWFzZWQgc3RhcnR1cCB0aW1lIGZvciB1cCB0byBhIGZld1xuICAgIHNlY29uZHMuXG4gICAgLS0+XG4gICAgPG1sb2NrX2V4ZWN1dGFibGU+dHJ1ZTwvbWxvY2tfZXhlY3V0YWJsZT5cblxuICAgIDwhLS0gUmVhbGxvY2F0ZSBtZW1vcnkgZm9yIG1hY2hpbmUgY29kZSAoXCJ0ZXh0XCIpIHVzaW5nIGh1Z2UgcGFnZXMuIEhpZ2hseSBleHBlcmltZW50YWwuIC0tPlxuICAgIDxyZW1hcF9leGVjdXRhYmxlPmZhbHNlPC9yZW1hcF9leGVjdXRhYmxlPlxuXG4gICAgPCFbQ0RBVEFbXG4gICAgICAgIFVuY29tbWVudCBiZWxvdyBpbiBvcmRlciB0byB1c2UgSkRCQyB0YWJsZSBlbmdpbmUgYW5kIGZ1bmN0aW9uLlxuXG4gICAgICAgIFRvIGluc3RhbGwgYW5kIHJ1biBKREJDIGJyaWRnZSBpbiBiYWNrZ3JvdW5kOlxuICAgICAgICAqIFtEZWJpYW4vVWJ1bnR1XVxuICAgICAgICAgIGV4cG9ydCBNVk5fVVJMPWh0dHBzOi8vcmVwbzEubWF2ZW4ub3JnL21hdmVuMi9ydS95YW5kZXgvY2xpY2tob3VzZS9jbGlja2hvdXNlLWpkYmMtYnJpZGdlXG4gICAgICAgICAgZXhwb3J0IFBLR19WRVI9JChjdXJsIC1zTCAkTVZOX1VSTC9tYXZlbi1tZXRhZGF0YS54bWwgfCBncmVwICc8cmVsZWFzZT4nIHwgc2VkIC1lICdzfC4qPlxcKC4qXFwpPC4qfFxcMXwnKVxuICAgICAgICAgIHdnZXQgaHR0cHM6Ly9naXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1qZGJjLWJyaWRnZS9yZWxlYXNlcy9kb3dubG9hZC92JFBLR19WRVIvY2xpY2tob3VzZS1qZGJjLWJyaWRnZV8kUEtHX1ZFUi0xX2FsbC5kZWJcbiAgICAgICAgICBhcHQgaW5zdGFsbCAtLW5vLWluc3RhbGwtcmVjb21tZW5kcyAtZiAuL2NsaWNraG91c2UtamRiYy1icmlkZ2VfJFBLR19WRVItMV9hbGwuZGViXG4gICAgICAgICAgY2xpY2tob3VzZS1qZGJjLWJyaWRnZSAmXG5cbiAgICAgICAgKiBbQ2VudE9TL1JIRUxdXG4gICAgICAgICAgZXhwb3J0IE1WTl9VUkw9aHR0cHM6Ly9yZXBvMS5tYXZlbi5vcmcvbWF2ZW4yL3J1L3lhbmRleC9jbGlja2hvdXNlL2NsaWNraG91c2UtamRiYy1icmlkZ2VcbiAgICAgICAgICBleHBvcnQgUEtHX1ZFUj0kKGN1cmwgLXNMICRNVk5fVVJML21hdmVuLW1ldGFkYXRhLnhtbCB8IGdyZXAgJzxyZWxlYXNlPicgfCBzZWQgLWUgJ3N8Lio+XFwoLipcXCk8Lip8XFwxfCcpXG4gICAgICAgICAgd2dldCBodHRwczovL2dpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWpkYmMtYnJpZGdlL3JlbGVhc2VzL2Rvd25sb2FkL3YkUEtHX1ZFUi9jbGlja2hvdXNlLWpkYmMtYnJpZGdlLSRQS0dfVkVSLTEubm9hcmNoLnJwbVxuICAgICAgICAgIHl1bSBsb2NhbGluc3RhbGwgLXkgY2xpY2tob3VzZS1qZGJjLWJyaWRnZS0kUEtHX1ZFUi0xLm5vYXJjaC5ycG1cbiAgICAgICAgICBjbGlja2hvdXNlLWpkYmMtYnJpZGdlICZcblxuICAgICAgICBQbGVhc2UgcmVmZXIgdG8gaHR0cHM6Ly9naXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1qZGJjLWJyaWRnZSN1c2FnZSBmb3IgbW9yZSBpbmZvcm1hdGlvbi5cbiAgICBdXT5cbiAgICA8IS0tXG4gICAgPGpkYmNfYnJpZGdlPlxuICAgICAgICA8aG9zdD4xMjcuMC4wLjE8L2hvc3Q+XG4gICAgICAgIDxwb3J0PjkwMTk8L3BvcnQ+XG4gICAgPC9qZGJjX2JyaWRnZT5cbiAgICAtLT5cblxuICAgIDwhLS0gQ29uZmlndXJhdGlvbiBvZiBjbHVzdGVycyB0aGF0IGNvdWxkIGJlIHVzZWQgaW4gRGlzdHJpYnV0ZWQgdGFibGVzLlxuICAgICAgICBodHRwczovL2NsaWNraG91c2UuY29tL2RvY3MvZW4vb3BlcmF0aW9ucy90YWJsZV9lbmdpbmVzL2Rpc3RyaWJ1dGVkL1xuICAgICAgLS0+XG4gICAgPHJlbW90ZV9zZXJ2ZXJzPlxuXG4gICAgICAgIDwhLS0gVGVzdCBvbmx5IHNoYXJkIGNvbmZpZyBmb3IgdGVzdGluZyBkaXN0cmlidXRlZCBzdG9yYWdlIC0tPlxuICAgICAgICA8cG9zdGhvZz5cbiAgICAgICAgICAgIDwhLS0gSW50ZXItc2VydmVyIHBlci1jbHVzdGVyIHNlY3JldCBmb3IgRGlzdHJpYnV0ZWQgcXVlcmllc1xuICAgICAgICAgICAgICAgIGRlZmF1bHQ6IG5vIHNlY3JldCAobm8gYXV0aGVudGljYXRpb24gd2lsbCBiZSBwZXJmb3JtZWQpXG5cbiAgICAgICAgICAgICAgICBJZiBzZXQsIHRoZW4gRGlzdHJpYnV0ZWQgcXVlcmllcyB3aWxsIGJlIHZhbGlkYXRlZCBvbiBzaGFyZHMsIHNvIGF0IGxlYXN0OlxuICAgICAgICAgICAgICAgIC0gc3VjaCBjbHVzdGVyIHNob3VsZCBleGlzdCBvbiB0aGUgc2hhcmQsXG4gICAgICAgICAgICAgICAgLSBzdWNoIGNsdXN0ZXIgc2hvdWxkIGhhdmUgdGhlIHNhbWUgc2VjcmV0LlxuXG4gICAgICAgICAgICAgICAgQW5kIGFsc28gKGFuZCB3aGljaCBpcyBtb3JlIGltcG9ydGFudCksIHRoZSBpbml0aWFsX3VzZXIgd2lsbFxuICAgICAgICAgICAgICAgIGJlIHVzZWQgYXMgY3VycmVudCB1c2VyIGZvciB0aGUgcXVlcnkuXG5cbiAgICAgICAgICAgICAgICBSaWdodCBub3cgdGhlIHByb3RvY29sIGlzIHByZXR0eSBzaW1wbGUgYW5kIGl0IG9ubHkgdGFrZXMgaW50byBhY2NvdW50OlxuICAgICAgICAgICAgICAgIC0gY2x1c3RlciBuYW1lXG4gICAgICAgICAgICAgICAgLSBxdWVyeVxuXG4gICAgICAgICAgICAgICAgQWxzbyBpdCB3aWxsIGJlIG5pY2UgaWYgdGhlIGZvbGxvd2luZyB3aWxsIGJlIGltcGxlbWVudGVkOlxuICAgICAgICAgICAgICAgIC0gc291cmNlIGhvc3RuYW1lIChzZWUgaW50ZXJzZXJ2ZXJfaHR0cF9ob3N0KSwgYnV0IHRoZW4gaXQgd2lsbCBkZXBlbmRzIGZyb20gRE5TLFxuICAgICAgICAgICAgICAgICAgaXQgY2FuIHVzZSBJUCBhZGRyZXNzIGluc3RlYWQsIGJ1dCB0aGVuIHRoZSB5b3UgbmVlZCB0byBnZXQgY29ycmVjdCBvbiB0aGUgaW5pdGlhdG9yIG5vZGUuXG4gICAgICAgICAgICAgICAgLSB0YXJnZXQgaG9zdG5hbWUgLyBpcCBhZGRyZXNzIChzYW1lIG5vdGVzIGFzIGZvciBzb3VyY2UgaG9zdG5hbWUpXG4gICAgICAgICAgICAgICAgLSB0aW1lLWJhc2VkIHNlY3VyaXR5IHRva2Vuc1xuICAgICAgICAgICAgLS0+XG4gICAgICAgICAgICA8IS0tIDxzZWNyZXQ+PC9zZWNyZXQ+IC0tPlxuXG4gICAgICAgICAgICA8c2hhcmQ+XG4gICAgICAgICAgICAgICAgPCEtLSBPcHRpb25hbC4gV2hldGhlciB0byB3cml0ZSBkYXRhIHRvIGp1c3Qgb25lIG9mIHRoZSByZXBsaWNhcy4gRGVmYXVsdDogZmFsc2VcbiAgICAgICAgICAgICAgICAod3JpdGUgZGF0YSB0byBhbGwgcmVwbGljYXMpLiAtLT5cbiAgICAgICAgICAgICAgICA8IS0tIDxpbnRlcm5hbF9yZXBsaWNhdGlvbj5mYWxzZTwvaW50ZXJuYWxfcmVwbGljYXRpb24+IC0tPlxuICAgICAgICAgICAgICAgIDwhLS0gT3B0aW9uYWwuIFNoYXJkIHdlaWdodCB3aGVuIHdyaXRpbmcgZGF0YS4gRGVmYXVsdDogMS4gLS0+XG4gICAgICAgICAgICAgICAgPCEtLSA8d2VpZ2h0PjE8L3dlaWdodD4gLS0+XG4gICAgICAgICAgICAgICAgPHJlcGxpY2E+XG4gICAgICAgICAgICAgICAgICAgIDxob3N0PmxvY2FsaG9zdDwvaG9zdD5cbiAgICAgICAgICAgICAgICAgICAgPHBvcnQ+OTAwMDwvcG9ydD5cbiAgICAgICAgICAgICAgICAgICAgPCEtLSBPcHRpb25hbC4gUHJpb3JpdHkgb2YgdGhlIHJlcGxpY2EgZm9yIGxvYWRfYmFsYW5jaW5nLiBEZWZhdWx0OiAxIChsZXNzXG4gICAgICAgICAgICAgICAgICAgIHZhbHVlIGhhcyBtb3JlIHByaW9yaXR5KS4gLS0+XG4gICAgICAgICAgICAgICAgICAgIDwhLS0gPHByaW9yaXR5PjE8L3ByaW9yaXR5PiAtLT5cbiAgICAgICAgICAgICAgICA8L3JlcGxpY2E+XG4gICAgICAgICAgICA8L3NoYXJkPlxuICAgICAgICA8L3Bvc3Rob2c+XG4gICAgPC9yZW1vdGVfc2VydmVycz5cblxuICAgIDwhLS0gVGhlIGxpc3Qgb2YgaG9zdHMgYWxsb3dlZCB0byB1c2UgaW4gVVJMLXJlbGF0ZWQgc3RvcmFnZSBlbmdpbmVzIGFuZCB0YWJsZSBmdW5jdGlvbnMuXG4gICAgICAgIElmIHRoaXMgc2VjdGlvbiBpcyBub3QgcHJlc2VudCBpbiBjb25maWd1cmF0aW9uLCBhbGwgaG9zdHMgYXJlIGFsbG93ZWQuXG4gICAgLS0+XG4gICAgPHJlbW90ZV91cmxfYWxsb3dfaG9zdHM+XG4gICAgICAgIDwhLS0gSG9zdCBzaG91bGQgYmUgc3BlY2lmaWVkIGV4YWN0bHkgYXMgaW4gVVJMLiBUaGUgbmFtZSBpcyBjaGVja2VkIGJlZm9yZSBETlMgcmVzb2x1dGlvbi5cbiAgICAgICAgICAgIEV4YW1wbGU6IFwieWFuZGV4LnJ1XCIsIFwieWFuZGV4LnJ1LlwiIGFuZCBcInd3dy55YW5kZXgucnVcIiBhcmUgZGlmZmVyZW50IGhvc3RzLlxuICAgICAgICAgICAgICAgICAgICBJZiBwb3J0IGlzIGV4cGxpY2l0bHkgc3BlY2lmaWVkIGluIFVSTCwgdGhlIGhvc3Q6cG9ydCBpcyBjaGVja2VkIGFzIGEgd2hvbGUuXG4gICAgICAgICAgICAgICAgICAgIElmIGhvc3Qgc3BlY2lmaWVkIGhlcmUgd2l0aG91dCBwb3J0LCBhbnkgcG9ydCB3aXRoIHRoaXMgaG9zdCBhbGxvd2VkLlxuICAgICAgICAgICAgICAgICAgICBcInlhbmRleC5ydVwiIC0+IFwieWFuZGV4LnJ1OjQ0M1wiLCBcInlhbmRleC5ydTo4MFwiIGV0Yy4gaXMgYWxsb3dlZCwgYnV0IFwieWFuZGV4LnJ1OjgwXCIgLT4gb25seVxuICAgICAgICBcInlhbmRleC5ydTo4MFwiIGlzIGFsbG93ZWQuXG4gICAgICAgICAgICBJZiB0aGUgaG9zdCBpcyBzcGVjaWZpZWQgYXMgSVAgYWRkcmVzcywgaXQgaXMgY2hlY2tlZCBhcyBzcGVjaWZpZWQgaW4gVVJMLiBFeGFtcGxlOlxuICAgICAgICBcIlsyYTAyOjZiODphOjphXVwiLlxuICAgICAgICAgICAgSWYgdGhlcmUgYXJlIHJlZGlyZWN0cyBhbmQgc3VwcG9ydCBmb3IgcmVkaXJlY3RzIGlzIGVuYWJsZWQsIGV2ZXJ5IHJlZGlyZWN0ICh0aGUgTG9jYXRpb24gZmllbGQpIGlzXG4gICAgICAgIGNoZWNrZWQuXG4gICAgICAgICAgICBIb3N0IHNob3VsZCBiZSBzcGVjaWZpZWQgdXNpbmcgdGhlIGhvc3QgeG1sIHRhZzpcbiAgICAgICAgICAgICAgICAgICAgPGhvc3Q+eWFuZGV4LnJ1PC9ob3N0PlxuICAgICAgICAtLT5cblxuICAgICAgICA8IS0tIFJlZ3VsYXIgZXhwcmVzc2lvbiBjYW4gYmUgc3BlY2lmaWVkLiBSRTIgZW5naW5lIGlzIHVzZWQgZm9yIHJlZ2V4cHMuXG4gICAgICAgICAgICBSZWdleHBzIGFyZSBub3QgYWxpZ25lZDogZG9uJ3QgZm9yZ2V0IHRvIGFkZCBeIGFuZCAkLiBBbHNvIGRvbid0IGZvcmdldCB0byBlc2NhcGUgZG90ICguKVxuICAgICAgICBtZXRhY2hhcmFjdGVyXG4gICAgICAgICAgICAoZm9yZ2V0dGluZyB0byBkbyBzbyBpcyBhIGNvbW1vbiBzb3VyY2Ugb2YgZXJyb3IpLlxuICAgICAgICAtLT5cbiAgICAgICAgPGhvc3RfcmVnZXhwPi4qPC9ob3N0X3JlZ2V4cD5cbiAgICA8L3JlbW90ZV91cmxfYWxsb3dfaG9zdHM+XG5cbiAgICA8IS0tIElmIGVsZW1lbnQgaGFzICdpbmNsJyBhdHRyaWJ1dGUsIHRoZW4gZm9yIGl0J3MgdmFsdWUgd2lsbCBiZSB1c2VkIGNvcnJlc3BvbmRpbmdcbiAgICBzdWJzdGl0dXRpb24gZnJvbSBhbm90aGVyIGZpbGUuXG4gICAgICAgIEJ5IGRlZmF1bHQsIHBhdGggdG8gZmlsZSB3aXRoIHN1YnN0aXR1dGlvbnMgaXMgL2V0Yy9tZXRyaWthLnhtbC4gSXQgY291bGQgYmUgY2hhbmdlZCBpbiBjb25maWcgaW5cbiAgICAnaW5jbHVkZV9mcm9tJyBlbGVtZW50LlxuICAgICAgICBWYWx1ZXMgZm9yIHN1YnN0aXR1dGlvbnMgYXJlIHNwZWNpZmllZCBpbiAvY2xpY2tob3VzZS9uYW1lX29mX3N1YnN0aXR1dGlvbiBlbGVtZW50cyBpbiB0aGF0IGZpbGUuXG4gICAgICAtLT5cblxuICAgIDwhLS0gWm9vS2VlcGVyIGlzIHVzZWQgdG8gc3RvcmUgbWV0YWRhdGEgYWJvdXQgcmVwbGljYXMsIHdoZW4gdXNpbmcgUmVwbGljYXRlZCB0YWJsZXMuXG4gICAgICAgIE9wdGlvbmFsLiBJZiB5b3UgZG9uJ3QgdXNlIHJlcGxpY2F0ZWQgdGFibGVzLCB5b3UgY291bGQgb21pdCB0aGF0LlxuXG4gICAgICAgIFNlZSBodHRwczovL2NsaWNraG91c2UuY29tL2RvY3MvZW4vZW5naW5lcy90YWJsZS1lbmdpbmVzL21lcmdldHJlZS1mYW1pbHkvcmVwbGljYXRpb24vXG4gICAgICAtLT5cblxuICAgIDx6b29rZWVwZXI+XG4gICAgICAgIDxub2RlPlxuICAgICAgICAgICAgPGhvc3Q+em9va2VlcGVyPC9ob3N0PlxuICAgICAgICAgICAgPHBvcnQ+MjE4MTwvcG9ydD5cbiAgICAgICAgPC9ub2RlPlxuICAgIDwvem9va2VlcGVyPlxuXG4gICAgPCEtLSBTdWJzdGl0dXRpb25zIGZvciBwYXJhbWV0ZXJzIG9mIHJlcGxpY2F0ZWQgdGFibGVzLlxuICAgICAgICAgIE9wdGlvbmFsLiBJZiB5b3UgZG9uJ3QgdXNlIHJlcGxpY2F0ZWQgdGFibGVzLCB5b3UgY291bGQgb21pdCB0aGF0LlxuXG4gICAgICAgIFNlZVxuICAgIGh0dHBzOi8vY2xpY2tob3VzZS5jb20vZG9jcy9lbi9lbmdpbmVzL3RhYmxlLWVuZ2luZXMvbWVyZ2V0cmVlLWZhbWlseS9yZXBsaWNhdGlvbi8jY3JlYXRpbmctcmVwbGljYXRlZC10YWJsZXNcbiAgICAgIC0tPlxuXG4gICAgPG1hY3Jvcz5cbiAgICAgICAgPHNoYXJkPjAxPC9zaGFyZD5cbiAgICAgICAgPHJlcGxpY2E+Y2gxPC9yZXBsaWNhPlxuICAgIDwvbWFjcm9zPlxuXG5cbiAgICA8IS0tIFJlbG9hZGluZyBpbnRlcnZhbCBmb3IgZW1iZWRkZWQgZGljdGlvbmFyaWVzLCBpbiBzZWNvbmRzLiBEZWZhdWx0OiAzNjAwLiAtLT5cbiAgICA8YnVpbHRpbl9kaWN0aW9uYXJpZXNfcmVsb2FkX2ludGVydmFsPjM2MDA8L2J1aWx0aW5fZGljdGlvbmFyaWVzX3JlbG9hZF9pbnRlcnZhbD5cblxuXG4gICAgPCEtLSBNYXhpbXVtIHNlc3Npb24gdGltZW91dCwgaW4gc2Vjb25kcy4gRGVmYXVsdDogMzYwMC4gLS0+XG4gICAgPG1heF9zZXNzaW9uX3RpbWVvdXQ+MzYwMDwvbWF4X3Nlc3Npb25fdGltZW91dD5cblxuICAgIDwhLS0gRGVmYXVsdCBzZXNzaW9uIHRpbWVvdXQsIGluIHNlY29uZHMuIERlZmF1bHQ6IDYwLiAtLT5cbiAgICA8ZGVmYXVsdF9zZXNzaW9uX3RpbWVvdXQ+NjA8L2RlZmF1bHRfc2Vzc2lvbl90aW1lb3V0PlxuXG4gICAgPCEtLSBTZW5kaW5nIGRhdGEgdG8gR3JhcGhpdGUgZm9yIG1vbml0b3JpbmcuIFNldmVyYWwgc2VjdGlvbnMgY2FuIGJlIGRlZmluZWQuIC0tPlxuICAgIDwhLS1cbiAgICAgICAgaW50ZXJ2YWwgLSBzZW5kIGV2ZXJ5IFggc2Vjb25kXG4gICAgICAgIHJvb3RfcGF0aCAtIHByZWZpeCBmb3Iga2V5c1xuICAgICAgICBob3N0bmFtZV9pbl9wYXRoIC0gYXBwZW5kIGhvc3RuYW1lIHRvIHJvb3RfcGF0aCAoZGVmYXVsdCA9IHRydWUpXG4gICAgICAgIG1ldHJpY3MgLSBzZW5kIGRhdGEgZnJvbSB0YWJsZSBzeXN0ZW0ubWV0cmljc1xuICAgICAgICBldmVudHMgLSBzZW5kIGRhdGEgZnJvbSB0YWJsZSBzeXN0ZW0uZXZlbnRzXG4gICAgICAgIGFzeW5jaHJvbm91c19tZXRyaWNzIC0gc2VuZCBkYXRhIGZyb20gdGFibGUgc3lzdGVtLmFzeW5jaHJvbm91c19tZXRyaWNzXG4gICAgLS0+XG4gICAgPCEtLVxuICAgIDxncmFwaGl0ZT5cbiAgICAgICAgPGhvc3Q+bG9jYWxob3N0PC9ob3N0PlxuICAgICAgICA8cG9ydD40MjAwMDwvcG9ydD5cbiAgICAgICAgPHRpbWVvdXQ+MC4xPC90aW1lb3V0PlxuICAgICAgICA8aW50ZXJ2YWw+NjA8L2ludGVydmFsPlxuICAgICAgICA8cm9vdF9wYXRoPm9uZV9taW48L3Jvb3RfcGF0aD5cbiAgICAgICAgPGhvc3RuYW1lX2luX3BhdGg+dHJ1ZTwvaG9zdG5hbWVfaW5fcGF0aD5cblxuICAgICAgICA8bWV0cmljcz50cnVlPC9tZXRyaWNzPlxuICAgICAgICA8ZXZlbnRzPnRydWU8L2V2ZW50cz5cbiAgICAgICAgPGV2ZW50c19jdW11bGF0aXZlPmZhbHNlPC9ldmVudHNfY3VtdWxhdGl2ZT5cbiAgICAgICAgPGFzeW5jaHJvbm91c19tZXRyaWNzPnRydWU8L2FzeW5jaHJvbm91c19tZXRyaWNzPlxuICAgIDwvZ3JhcGhpdGU+XG4gICAgPGdyYXBoaXRlPlxuICAgICAgICA8aG9zdD5sb2NhbGhvc3Q8L2hvc3Q+XG4gICAgICAgIDxwb3J0PjQyMDAwPC9wb3J0PlxuICAgICAgICA8dGltZW91dD4wLjE8L3RpbWVvdXQ+XG4gICAgICAgIDxpbnRlcnZhbD4xPC9pbnRlcnZhbD5cbiAgICAgICAgPHJvb3RfcGF0aD5vbmVfc2VjPC9yb290X3BhdGg+XG5cbiAgICAgICAgPG1ldHJpY3M+dHJ1ZTwvbWV0cmljcz5cbiAgICAgICAgPGV2ZW50cz50cnVlPC9ldmVudHM+XG4gICAgICAgIDxldmVudHNfY3VtdWxhdGl2ZT5mYWxzZTwvZXZlbnRzX2N1bXVsYXRpdmU+XG4gICAgICAgIDxhc3luY2hyb25vdXNfbWV0cmljcz5mYWxzZTwvYXN5bmNocm9ub3VzX21ldHJpY3M+XG4gICAgPC9ncmFwaGl0ZT5cbiAgICAtLT5cblxuICAgIDwhLS0gU2VydmUgZW5kcG9pbnQgZm9yIFByb21ldGhldXMgbW9uaXRvcmluZy4gLS0+XG4gICAgPCEtLVxuICAgICAgICBlbmRwb2ludCAtIG1lcnRpY3MgcGF0aCAocmVsYXRpdmUgdG8gcm9vdCwgc3RhdHJpbmcgd2l0aCBcIi9cIilcbiAgICAgICAgcG9ydCAtIHBvcnQgdG8gc2V0dXAgc2VydmVyLiBJZiBub3QgZGVmaW5lZCBvciAwIHRoYW4gaHR0cF9wb3J0IHVzZWRcbiAgICAgICAgbWV0cmljcyAtIHNlbmQgZGF0YSBmcm9tIHRhYmxlIHN5c3RlbS5tZXRyaWNzXG4gICAgICAgIGV2ZW50cyAtIHNlbmQgZGF0YSBmcm9tIHRhYmxlIHN5c3RlbS5ldmVudHNcbiAgICAgICAgYXN5bmNocm9ub3VzX21ldHJpY3MgLSBzZW5kIGRhdGEgZnJvbSB0YWJsZSBzeXN0ZW0uYXN5bmNocm9ub3VzX21ldHJpY3NcbiAgICAgICAgc3RhdHVzX2luZm8gLSBzZW5kIGRhdGEgZnJvbSBkaWZmZXJlbnQgY29tcG9uZW50IGZyb20gQ0gsIGV4OiBEaWN0aW9uYXJpZXMgc3RhdHVzXG4gICAgLS0+XG4gICAgPCEtLVxuICAgIDxwcm9tZXRoZXVzPlxuICAgICAgICA8ZW5kcG9pbnQ+L21ldHJpY3M8L2VuZHBvaW50PlxuICAgICAgICA8cG9ydD45MzYzPC9wb3J0PlxuXG4gICAgICAgIDxtZXRyaWNzPnRydWU8L21ldHJpY3M+XG4gICAgICAgIDxldmVudHM+dHJ1ZTwvZXZlbnRzPlxuICAgICAgICA8YXN5bmNocm9ub3VzX21ldHJpY3M+dHJ1ZTwvYXN5bmNocm9ub3VzX21ldHJpY3M+XG4gICAgICAgIDxzdGF0dXNfaW5mbz50cnVlPC9zdGF0dXNfaW5mbz5cbiAgICA8L3Byb21ldGhldXM+XG4gICAgLS0+XG5cbiAgICA8IS0tIFF1ZXJ5IGxvZy4gVXNlZCBvbmx5IGZvciBxdWVyaWVzIHdpdGggc2V0dGluZyBsb2dfcXVlcmllcyA9IDEuIC0tPlxuICAgIDxxdWVyeV9sb2c+XG4gICAgICAgIDwhLS0gV2hhdCB0YWJsZSB0byBpbnNlcnQgZGF0YS4gSWYgdGFibGUgaXMgbm90IGV4aXN0LCBpdCB3aWxsIGJlIGNyZWF0ZWQuXG4gICAgICAgICAgICBXaGVuIHF1ZXJ5IGxvZyBzdHJ1Y3R1cmUgaXMgY2hhbmdlZCBhZnRlciBzeXN0ZW0gdXBkYXRlLFxuICAgICAgICAgICAgICB0aGVuIG9sZCB0YWJsZSB3aWxsIGJlIHJlbmFtZWQgYW5kIG5ldyB0YWJsZSB3aWxsIGJlIGNyZWF0ZWQgYXV0b21hdGljYWxseS5cbiAgICAgICAgLS0+XG4gICAgICAgIDxkYXRhYmFzZT5zeXN0ZW08L2RhdGFiYXNlPlxuICAgICAgICA8dGFibGU+cXVlcnlfbG9nPC90YWJsZT5cbiAgICAgICAgPCEtLVxuICAgICAgICAgICAgUEFSVElUSU9OIEJZIGV4cHI6XG4gICAgICAgIGh0dHBzOi8vY2xpY2tob3VzZS5jb20vZG9jcy9lbi90YWJsZV9lbmdpbmVzL21lcmdldHJlZS1mYW1pbHkvY3VzdG9tX3BhcnRpdGlvbmluZ19rZXkvXG4gICAgICAgICAgICBFeGFtcGxlOlxuICAgICAgICAgICAgICAgIGV2ZW50X2RhdGVcbiAgICAgICAgICAgICAgICB0b01vbmRheShldmVudF9kYXRlKVxuICAgICAgICAgICAgICAgIHRvWVlZWU1NKGV2ZW50X2RhdGUpXG4gICAgICAgICAgICAgICAgdG9TdGFydE9mSG91cihldmVudF90aW1lKVxuICAgICAgICAtLT5cbiAgICAgICAgPHBhcnRpdGlvbl9ieT50b1lZWVlNTShldmVudF9kYXRlKTwvcGFydGl0aW9uX2J5PlxuICAgICAgICA8IS0tXG4gICAgICAgICAgICBUYWJsZSBUVEwgc3BlY2lmaWNhdGlvbjpcbiAgICAgICAgaHR0cHM6Ly9jbGlja2hvdXNlLmNvbS9kb2NzL2VuL2VuZ2luZXMvdGFibGUtZW5naW5lcy9tZXJnZXRyZWUtZmFtaWx5L21lcmdldHJlZS8jbWVyZ2V0cmVlLXRhYmxlLXR0bFxuICAgICAgICAgICAgRXhhbXBsZTpcbiAgICAgICAgICAgICAgICBldmVudF9kYXRlICsgSU5URVJWQUwgMSBXRUVLXG4gICAgICAgICAgICAgICAgZXZlbnRfZGF0ZSArIElOVEVSVkFMIDcgREFZIERFTEVURVxuICAgICAgICAgICAgICAgIGV2ZW50X2RhdGUgKyBJTlRFUlZBTCAyIFdFRUsgVE8gRElTSyAnYmJiJ1xuXG4gICAgICAgIDx0dGw+ZXZlbnRfZGF0ZSArIElOVEVSVkFMIDMwIERBWSBERUxFVEU8L3R0bD5cbiAgICAgICAgLS0+XG5cbiAgICAgICAgPCEtLSBJbnN0ZWFkIG9mIHBhcnRpdGlvbl9ieSwgeW91IGNhbiBwcm92aWRlIGZ1bGwgZW5naW5lIGV4cHJlc3Npb24gKHN0YXJ0aW5nIHdpdGggRU5HSU5FID1cbiAgICAgICAgKSB3aXRoIHBhcmFtZXRlcnMsXG4gICAgICAgICAgICBFeGFtcGxlOiA8ZW5naW5lPkVOR0lORSA9IE1lcmdlVHJlZSBQQVJUSVRJT04gQlkgdG9ZWVlZTU0oZXZlbnRfZGF0ZSkgT1JERVIgQlkgKGV2ZW50X2RhdGUsXG4gICAgICAgIGV2ZW50X3RpbWUpIFNFVFRJTkdTIGluZGV4X2dyYW51bGFyaXR5ID0gMTAyNDwvZW5naW5lPlxuICAgICAgICAgIC0tPlxuXG4gICAgICAgIDwhLS0gSW50ZXJ2YWwgb2YgZmx1c2hpbmcgZGF0YS4gLS0+XG4gICAgICAgIDxmbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+NzUwMDwvZmx1c2hfaW50ZXJ2YWxfbWlsbGlzZWNvbmRzPlxuICAgIDwvcXVlcnlfbG9nPlxuXG4gICAgPCEtLSBUcmFjZSBsb2cuIFN0b3JlcyBzdGFjayB0cmFjZXMgY29sbGVjdGVkIGJ5IHF1ZXJ5IHByb2ZpbGVycy5cbiAgICAgICAgU2VlIHF1ZXJ5X3Byb2ZpbGVyX3JlYWxfdGltZV9wZXJpb2RfbnMgYW5kIHF1ZXJ5X3Byb2ZpbGVyX2NwdV90aW1lX3BlcmlvZF9ucyBzZXR0aW5ncy4gLS0+XG4gICAgPHRyYWNlX2xvZz5cbiAgICAgICAgPGRhdGFiYXNlPnN5c3RlbTwvZGF0YWJhc2U+XG4gICAgICAgIDx0YWJsZT50cmFjZV9sb2c8L3RhYmxlPlxuXG4gICAgICAgIDxwYXJ0aXRpb25fYnk+dG9ZWVlZTU0oZXZlbnRfZGF0ZSk8L3BhcnRpdGlvbl9ieT5cbiAgICAgICAgPGZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz43NTAwPC9mbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+XG4gICAgPC90cmFjZV9sb2c+XG5cbiAgICA8IS0tIFF1ZXJ5IHRocmVhZCBsb2cuIEhhcyBpbmZvcm1hdGlvbiBhYm91dCBhbGwgdGhyZWFkcyBwYXJ0aWNpcGF0ZWQgaW4gcXVlcnkgZXhlY3V0aW9uLlxuICAgICAgICBVc2VkIG9ubHkgZm9yIHF1ZXJpZXMgd2l0aCBzZXR0aW5nIGxvZ19xdWVyeV90aHJlYWRzID0gMS4gLS0+XG4gICAgPHF1ZXJ5X3RocmVhZF9sb2c+XG4gICAgICAgIDxkYXRhYmFzZT5zeXN0ZW08L2RhdGFiYXNlPlxuICAgICAgICA8dGFibGU+cXVlcnlfdGhyZWFkX2xvZzwvdGFibGU+XG4gICAgICAgIDxwYXJ0aXRpb25fYnk+dG9ZWVlZTU0oZXZlbnRfZGF0ZSk8L3BhcnRpdGlvbl9ieT5cbiAgICAgICAgPGZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz43NTAwPC9mbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+XG4gICAgPC9xdWVyeV90aHJlYWRfbG9nPlxuXG4gICAgPCEtLSBRdWVyeSB2aWV3cyBsb2cuIEhhcyBpbmZvcm1hdGlvbiBhYm91dCBhbGwgZGVwZW5kZW50IHZpZXdzIGFzc29jaWF0ZWQgd2l0aCBhIHF1ZXJ5LlxuICAgICAgICBVc2VkIG9ubHkgZm9yIHF1ZXJpZXMgd2l0aCBzZXR0aW5nIGxvZ19xdWVyeV92aWV3cyA9IDEuIC0tPlxuICAgIDxxdWVyeV92aWV3c19sb2c+XG4gICAgICAgIDxkYXRhYmFzZT5zeXN0ZW08L2RhdGFiYXNlPlxuICAgICAgICA8dGFibGU+cXVlcnlfdmlld3NfbG9nPC90YWJsZT5cbiAgICAgICAgPHBhcnRpdGlvbl9ieT50b1lZWVlNTShldmVudF9kYXRlKTwvcGFydGl0aW9uX2J5PlxuICAgICAgICA8Zmx1c2hfaW50ZXJ2YWxfbWlsbGlzZWNvbmRzPjc1MDA8L2ZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz5cbiAgICA8L3F1ZXJ5X3ZpZXdzX2xvZz5cblxuICAgIDwhLS0gVW5jb21tZW50IGlmIHVzZSBwYXJ0IGxvZy5cbiAgICAgICAgUGFydCBsb2cgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgYWxsIGFjdGlvbnMgd2l0aCBwYXJ0cyBpbiBNZXJnZVRyZWUgdGFibGVzIChjcmVhdGlvbiwgZGVsZXRpb24sXG4gICAgbWVyZ2VzLCBkb3dubG9hZHMpLi0tPlxuICAgIDxwYXJ0X2xvZz5cbiAgICAgICAgPGRhdGFiYXNlPnN5c3RlbTwvZGF0YWJhc2U+XG4gICAgICAgIDx0YWJsZT5wYXJ0X2xvZzwvdGFibGU+XG4gICAgICAgIDxwYXJ0aXRpb25fYnk+dG9ZWVlZTU0oZXZlbnRfZGF0ZSk8L3BhcnRpdGlvbl9ieT5cbiAgICAgICAgPGZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz43NTAwPC9mbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+XG4gICAgPC9wYXJ0X2xvZz5cblxuICAgIDwhLS0gVW5jb21tZW50IHRvIHdyaXRlIHRleHQgbG9nIGludG8gdGFibGUuXG4gICAgICAgIFRleHQgbG9nIGNvbnRhaW5zIGFsbCBpbmZvcm1hdGlvbiBmcm9tIHVzdWFsIHNlcnZlciBsb2cgYnV0IHN0b3JlcyBpdCBpbiBzdHJ1Y3R1cmVkIGFuZCBlZmZpY2llbnRcbiAgICB3YXkuXG4gICAgICAgIFRoZSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCBnb2VzIHRvIHRoZSB0YWJsZSBjYW4gYmUgbGltaXRlZCAoPGxldmVsPiksIGlmIG5vdCBzcGVjaWZpZWQgYWxsXG4gICAgbWVzc2FnZXMgd2lsbCBnbyB0byB0aGUgdGFibGUuXG4gICAgPHRleHRfbG9nPlxuICAgICAgICA8ZGF0YWJhc2U+c3lzdGVtPC9kYXRhYmFzZT5cbiAgICAgICAgPHRhYmxlPnRleHRfbG9nPC90YWJsZT5cbiAgICAgICAgPGZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz43NTAwPC9mbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+XG4gICAgICAgIDxsZXZlbD48L2xldmVsPlxuICAgIDwvdGV4dF9sb2c+XG4gICAgLS0+XG5cbiAgICA8IS0tIE1ldHJpYyBsb2cgY29udGFpbnMgcm93cyB3aXRoIGN1cnJlbnQgdmFsdWVzIG9mIFByb2ZpbGVFdmVudHMsIEN1cnJlbnRNZXRyaWNzIGNvbGxlY3RlZFxuICAgIHdpdGggXCJjb2xsZWN0X2ludGVydmFsX21pbGxpc2Vjb25kc1wiIGludGVydmFsLiAtLT5cbiAgICA8bWV0cmljX2xvZz5cbiAgICAgICAgPGRhdGFiYXNlPnN5c3RlbTwvZGF0YWJhc2U+XG4gICAgICAgIDx0YWJsZT5tZXRyaWNfbG9nPC90YWJsZT5cbiAgICAgICAgPGZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz43NTAwPC9mbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+XG4gICAgICAgIDxjb2xsZWN0X2ludGVydmFsX21pbGxpc2Vjb25kcz4xMDAwPC9jb2xsZWN0X2ludGVydmFsX21pbGxpc2Vjb25kcz5cbiAgICA8L21ldHJpY19sb2c+XG5cbiAgICA8IS0tXG4gICAgICAgIEFzeW5jaHJvbm91cyBtZXRyaWMgbG9nIGNvbnRhaW5zIHZhbHVlcyBvZiBtZXRyaWNzIGZyb21cbiAgICAgICAgc3lzdGVtLmFzeW5jaHJvbm91c19tZXRyaWNzLlxuICAgIC0tPlxuICAgIDxhc3luY2hyb25vdXNfbWV0cmljX2xvZz5cbiAgICAgICAgPGRhdGFiYXNlPnN5c3RlbTwvZGF0YWJhc2U+XG4gICAgICAgIDx0YWJsZT5hc3luY2hyb25vdXNfbWV0cmljX2xvZzwvdGFibGU+XG4gICAgICAgIDwhLS1cbiAgICAgICAgICAgIEFzeW5jaHJvbm91cyBtZXRyaWNzIGFyZSB1cGRhdGVkIG9uY2UgYSBtaW51dGUsIHNvIHRoZXJlIGlzXG4gICAgICAgICAgICBubyBuZWVkIHRvIGZsdXNoIG1vcmUgb2Z0ZW4uXG4gICAgICAgIC0tPlxuICAgICAgICA8Zmx1c2hfaW50ZXJ2YWxfbWlsbGlzZWNvbmRzPjcwMDA8L2ZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz5cbiAgICA8L2FzeW5jaHJvbm91c19tZXRyaWNfbG9nPlxuXG4gICAgPCEtLVxuICAgICAgICBPcGVuVGVsZW1ldHJ5IGxvZyBjb250YWlucyBPcGVuVGVsZW1ldHJ5IHRyYWNlIHNwYW5zLlxuICAgIC0tPlxuICAgIDxvcGVudGVsZW1ldHJ5X3NwYW5fbG9nPlxuICAgICAgICA8IS0tXG4gICAgICAgICAgICBUaGUgZGVmYXVsdCB0YWJsZSBjcmVhdGlvbiBjb2RlIGlzIGluc3VmZmljaWVudCwgdGhpcyA8ZW5naW5lPiBzcGVjXG4gICAgICAgICAgICBpcyBhIHdvcmthcm91bmQuIFRoZXJlIGlzIG5vICdldmVudF90aW1lJyBmb3IgdGhpcyBsb2csIGJ1dCB0d28gdGltZXMsXG4gICAgICAgICAgICBzdGFydCBhbmQgZmluaXNoLiBJdCBpcyBzb3J0ZWQgYnkgZmluaXNoIHRpbWUsIHRvIGF2b2lkIGluc2VydGluZ1xuICAgICAgICAgICAgZGF0YSB0b28gZmFyIGF3YXkgaW4gdGhlIHBhc3QgKHByb2JhYmx5IHdlIGNhbiBzb21ldGltZXMgaW5zZXJ0IGEgc3BhblxuICAgICAgICAgICAgdGhhdCBpcyBzZWNvbmRzIGVhcmxpZXIgdGhhbiB0aGUgbGFzdCBzcGFuIGluIHRoZSB0YWJsZSwgZHVlIHRvIGEgcmFjZVxuICAgICAgICAgICAgYmV0d2VlbiBzZXZlcmFsIHNwYW5zIGluc2VydGVkIGluIHBhcmFsbGVsKS4gVGhpcyBnaXZlcyB0aGUgc3BhbnMgYVxuICAgICAgICAgICAgZ2xvYmFsIG9yZGVyIHRoYXQgd2UgY2FuIHVzZSB0byBlLmcuIHJldHJ5IGluc2VydGlvbiBpbnRvIHNvbWUgZXh0ZXJuYWxcbiAgICAgICAgICAgIHN5c3RlbS5cbiAgICAgICAgLS0+XG4gICAgICAgIDxlbmdpbmU+XG4gICAgICAgICAgICBlbmdpbmUgTWVyZ2VUcmVlXG4gICAgICAgICAgICBwYXJ0aXRpb24gYnkgdG9ZWVlZTU0oZmluaXNoX2RhdGUpXG4gICAgICAgICAgICBvcmRlciBieSAoZmluaXNoX2RhdGUsIGZpbmlzaF90aW1lX3VzLCB0cmFjZV9pZClcbiAgICAgICAgPC9lbmdpbmU+XG4gICAgICAgIDxkYXRhYmFzZT5zeXN0ZW08L2RhdGFiYXNlPlxuICAgICAgICA8dGFibGU+b3BlbnRlbGVtZXRyeV9zcGFuX2xvZzwvdGFibGU+XG4gICAgICAgIDxmbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+NzUwMDwvZmx1c2hfaW50ZXJ2YWxfbWlsbGlzZWNvbmRzPlxuICAgIDwvb3BlbnRlbGVtZXRyeV9zcGFuX2xvZz5cblxuXG4gICAgPCEtLSBDcmFzaCBsb2cuIFN0b3JlcyBzdGFjayB0cmFjZXMgZm9yIGZhdGFsIGVycm9ycy5cbiAgICAgICAgVGhpcyB0YWJsZSBpcyBub3JtYWxseSBlbXB0eS4gLS0+XG4gICAgPGNyYXNoX2xvZz5cbiAgICAgICAgPGRhdGFiYXNlPnN5c3RlbTwvZGF0YWJhc2U+XG4gICAgICAgIDx0YWJsZT5jcmFzaF9sb2c8L3RhYmxlPlxuXG4gICAgICAgIDxwYXJ0aXRpb25fYnkgLz5cbiAgICAgICAgPGZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz4xMDAwPC9mbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+XG4gICAgPC9jcmFzaF9sb2c+XG5cbiAgICA8IS0tIFNlc3Npb24gbG9nLiBTdG9yZXMgdXNlciBsb2cgaW4gKHN1Y2Nlc3NmdWwgb3Igbm90KSBhbmQgbG9nIG91dCBldmVudHMuIC0tPlxuICAgIDxzZXNzaW9uX2xvZz5cbiAgICAgICAgPGRhdGFiYXNlPnN5c3RlbTwvZGF0YWJhc2U+XG4gICAgICAgIDx0YWJsZT5zZXNzaW9uX2xvZzwvdGFibGU+XG5cbiAgICAgICAgPHBhcnRpdGlvbl9ieT50b1lZWVlNTShldmVudF9kYXRlKTwvcGFydGl0aW9uX2J5PlxuICAgICAgICA8Zmx1c2hfaW50ZXJ2YWxfbWlsbGlzZWNvbmRzPjc1MDA8L2ZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz5cbiAgICA8L3Nlc3Npb25fbG9nPlxuXG4gICAgPCEtLSBQYXJhbWV0ZXJzIGZvciBlbWJlZGRlZCBkaWN0aW9uYXJpZXMsIHVzZWQgaW4gWWFuZGV4Lk1ldHJpY2EuXG4gICAgICAgIFNlZSBodHRwczovL2NsaWNraG91c2UuY29tL2RvY3MvZW4vZGljdHMvaW50ZXJuYWxfZGljdHMvXG4gICAgLS0+XG5cbiAgICA8IS0tIFBhdGggdG8gZmlsZSB3aXRoIHJlZ2lvbiBoaWVyYXJjaHkuIC0tPlxuICAgIDwhLS1cbiAgICA8cGF0aF90b19yZWdpb25zX2hpZXJhcmNoeV9maWxlPi9vcHQvZ2VvL3JlZ2lvbnNfaGllcmFyY2h5LnR4dDwvcGF0aF90b19yZWdpb25zX2hpZXJhcmNoeV9maWxlPiAtLT5cblxuICAgIDwhLS0gUGF0aCB0byBkaXJlY3Rvcnkgd2l0aCBmaWxlcyBjb250YWluaW5nIG5hbWVzIG9mIHJlZ2lvbnMgLS0+XG4gICAgPCEtLSA8cGF0aF90b19yZWdpb25zX25hbWVzX2ZpbGVzPi9vcHQvZ2VvLzwvcGF0aF90b19yZWdpb25zX25hbWVzX2ZpbGVzPiAtLT5cblxuXG4gICAgPCEtLSA8dG9wX2xldmVsX2RvbWFpbnNfcGF0aD4vdmFyL2xpYi9jbGlja2hvdXNlL3RvcF9sZXZlbF9kb21haW5zLzwvdG9wX2xldmVsX2RvbWFpbnNfcGF0aD4gLS0+XG4gICAgPCEtLSBDdXN0b20gVExEIGxpc3RzLlxuICAgICAgICBGb3JtYXQ6IDxuYW1lPi9wYXRoL3RvL2ZpbGU8L25hbWU+XG5cbiAgICAgICAgQ2hhbmdlcyB3aWxsIG5vdCBiZSBhcHBsaWVkIHcvbyBzZXJ2ZXIgcmVzdGFydC5cbiAgICAgICAgUGF0aCB0byB0aGUgbGlzdCBpcyB1bmRlciB0b3BfbGV2ZWxfZG9tYWluc19wYXRoIChzZWUgYWJvdmUpLlxuICAgIC0tPlxuICAgIDx0b3BfbGV2ZWxfZG9tYWluc19saXN0cz5cbiAgICAgICAgPCEtLVxuICAgICAgICA8cHVibGljX3N1ZmZpeF9saXN0Pi9wYXRoL3RvL3B1YmxpY19zdWZmaXhfbGlzdC5kYXQ8L3B1YmxpY19zdWZmaXhfbGlzdD5cbiAgICAgICAgLS0+XG4gICAgPC90b3BfbGV2ZWxfZG9tYWluc19saXN0cz5cblxuICAgIDwhLS0gQ29uZmlndXJhdGlvbiBvZiBleHRlcm5hbCBkaWN0aW9uYXJpZXMuIFNlZTpcbiAgICAgICAgaHR0cHM6Ly9jbGlja2hvdXNlLmNvbS9kb2NzL2VuL3NxbC1yZWZlcmVuY2UvZGljdGlvbmFyaWVzL2V4dGVybmFsLWRpY3Rpb25hcmllcy9leHRlcm5hbC1kaWN0c1xuICAgIC0tPlxuICAgIDxkaWN0aW9uYXJpZXNfY29uZmlnPipfZGljdGlvbmFyeS54bWw8L2RpY3Rpb25hcmllc19jb25maWc+XG5cbiAgICA8IS0tIENvbmZpZ3VyYXRpb24gb2YgdXNlciBkZWZpbmVkIGV4ZWN1dGFibGUgZnVuY3Rpb25zIC0tPlxuICAgIDx1c2VyX2RlZmluZWRfZXhlY3V0YWJsZV9mdW5jdGlvbnNfY29uZmlnPipfZnVuY3Rpb24ueG1sPC91c2VyX2RlZmluZWRfZXhlY3V0YWJsZV9mdW5jdGlvbnNfY29uZmlnPlxuXG4gICAgPCEtLSBVbmNvbW1lbnQgaWYgeW91IHdhbnQgZGF0YSB0byBiZSBjb21wcmVzc2VkIDMwLTEwMCUgYmV0dGVyLlxuICAgICAgICBEb24ndCBkbyB0aGF0IGlmIHlvdSBqdXN0IHN0YXJ0ZWQgdXNpbmcgQ2xpY2tIb3VzZS5cbiAgICAgIC0tPlxuICAgIDwhLS1cbiAgICA8Y29tcHJlc3Npb24+XG4gICAgICAgIDwhLSAtIFNldCBvZiB2YXJpYW50cy4gQ2hlY2tlZCBpbiBvcmRlci4gTGFzdCBtYXRjaGluZyBjYXNlIHdpbnMuIElmIG5vdGhpbmcgbWF0Y2hlcywgbHo0IHdpbGwgYmVcbiAgICB1c2VkLiAtIC0+XG4gICAgICAgIDxjYXNlPlxuXG4gICAgICAgICAgICA8IS0gLSBDb25kaXRpb25zLiBBbGwgbXVzdCBiZSBzYXRpc2ZpZWQuIFNvbWUgY29uZGl0aW9ucyBtYXkgYmUgb21pdHRlZC4gLSAtPlxuICAgICAgICAgICAgPG1pbl9wYXJ0X3NpemU+MTAwMDAwMDAwMDA8L21pbl9wYXJ0X3NpemU+ICAgICAgICA8IS0gLSBNaW4gcGFydCBzaXplIGluIGJ5dGVzLiAtIC0+XG4gICAgICAgICAgICA8bWluX3BhcnRfc2l6ZV9yYXRpbz4wLjAxPC9taW5fcGFydF9zaXplX3JhdGlvPiAgIDwhLSAtIE1pbiBzaXplIG9mIHBhcnQgcmVsYXRpdmUgdG8gd2hvbGUgdGFibGVcbiAgICBzaXplLiAtIC0+XG5cbiAgICAgICAgICAgIDwhLSAtIFdoYXQgY29tcHJlc3Npb24gbWV0aG9kIHRvIHVzZS4gLSAtPlxuICAgICAgICAgICAgPG1ldGhvZD56c3RkPC9tZXRob2Q+XG4gICAgICAgIDwvY2FzZT5cbiAgICA8L2NvbXByZXNzaW9uPlxuICAgIC0tPlxuXG4gICAgPCEtLSBDb25maWd1cmF0aW9uIG9mIGVuY3J5cHRpb24uIFRoZSBzZXJ2ZXIgZXhlY3V0ZXMgYSBjb21tYW5kIHRvXG4gICAgICAgIG9idGFpbiBhbiBlbmNyeXB0aW9uIGtleSBhdCBzdGFydHVwIGlmIHN1Y2ggYSBjb21tYW5kIGlzXG4gICAgICAgIGRlZmluZWQsIG9yIGVuY3J5cHRpb24gY29kZWNzIHdpbGwgYmUgZGlzYWJsZWQgb3RoZXJ3aXNlLiBUaGVcbiAgICAgICAgY29tbWFuZCBpcyBleGVjdXRlZCB0aHJvdWdoIC9iaW4vc2ggYW5kIGlzIGV4cGVjdGVkIHRvIHdyaXRlXG4gICAgICAgIGEgQmFzZTY0LWVuY29kZWQga2V5IHRvIHRoZSBzdGRvdXQuIC0tPlxuICAgIDxlbmNyeXB0aW9uX2NvZGVjcz5cbiAgICAgICAgPCEtLSBhZXNfMTI4X2djbV9zaXYgLS0+XG4gICAgICAgIDwhLS0gRXhhbXBsZSBvZiBnZXR0aW5nIGhleCBrZXkgZnJvbSBlbnYgLS0+XG4gICAgICAgIDwhLS0gdGhlIGNvZGUgc2hvdWxkIHVzZSB0aGlzIGtleSBhbmQgdGhyb3cgYW4gZXhjZXB0aW9uIGlmIGl0cyBsZW5ndGggaXMgbm90IDE2IGJ5dGVzIC0tPlxuICAgICAgICA8IS0ta2V5X2hleFxuICAgICAgICBmcm9tX2Vudj1cIi4uLlwiPjwva2V5X2hleCAtLT5cblxuICAgICAgICA8IS0tIEV4YW1wbGUgb2YgbXVsdGlwbGUgaGV4IGtleXMuIFRoZXkgY2FuIGJlIGltcG9ydGVkIGZyb20gZW52IG9yIGJlIHdyaXR0ZW4gZG93biBpblxuICAgICAgICBjb25maWctLT5cbiAgICAgICAgPCEtLSB0aGUgY29kZSBzaG91bGQgdXNlIHRoZXNlIGtleXMgYW5kIHRocm93IGFuIGV4Y2VwdGlvbiBpZiB0aGVpciBsZW5ndGggaXMgbm90IDE2IGJ5dGVzIC0tPlxuICAgICAgICA8IS0tIGtleV9oZXggaWQ9XCIwXCI+Li4uPC9rZXlfaGV4IC0tPlxuICAgICAgICA8IS0tIGtleV9oZXggaWQ9XCIxXCIgZnJvbV9lbnY9XCIuLlwiPjwva2V5X2hleCAtLT5cbiAgICAgICAgPCEtLSBrZXlfaGV4IGlkPVwiMlwiPi4uLjwva2V5X2hleCAtLT5cbiAgICAgICAgPCEtLSBjdXJyZW50X2tleV9pZD4yPC9jdXJyZW50X2tleV9pZCAtLT5cblxuICAgICAgICA8IS0tIEV4YW1wbGUgb2YgZ2V0dGluZyBoZXgga2V5IGZyb20gY29uZmlnIC0tPlxuICAgICAgICA8IS0tIHRoZSBjb2RlIHNob3VsZCB1c2UgdGhpcyBrZXkgYW5kIHRocm93IGFuIGV4Y2VwdGlvbiBpZiBpdHMgbGVuZ3RoIGlzIG5vdCAxNiBieXRlcyAtLT5cbiAgICAgICAgPCEtLSBrZXk+Li4uPC9rZXkgLS0+XG5cbiAgICAgICAgPCEtLSBleGFtcGxlIG9mIGFkZGluZyBub25jZSAtLT5cbiAgICAgICAgPCEtLSBub25jZT4uLi48L25vbmNlIC0tPlxuXG4gICAgICAgIDwhLS0gL2Flc18xMjhfZ2NtX3NpdiAtLT5cbiAgICA8L2VuY3J5cHRpb25fY29kZWNzPlxuXG4gICAgPCEtLSBBbGxvdyB0byBleGVjdXRlIGRpc3RyaWJ1dGVkIERETCBxdWVyaWVzIChDUkVBVEUsIERST1AsIEFMVEVSLCBSRU5BTUUpIG9uIGNsdXN0ZXIuXG4gICAgICAgIFdvcmtzIG9ubHkgaWYgWm9vS2VlcGVyIGlzIGVuYWJsZWQuIENvbW1lbnQgaXQgaWYgc3VjaCBmdW5jdGlvbmFsaXR5IGlzbid0IHJlcXVpcmVkLiAtLT5cbiAgICA8ZGlzdHJpYnV0ZWRfZGRsPlxuICAgICAgICA8IS0tIFBhdGggaW4gWm9vS2VlcGVyIHRvIHF1ZXVlIHdpdGggRERMIHF1ZXJpZXMgLS0+XG4gICAgICAgIDxwYXRoPi9jbGlja2hvdXNlL3Rhc2tfcXVldWUvZGRsPC9wYXRoPlxuXG4gICAgICAgIDwhLS0gU2V0dGluZ3MgZnJvbSB0aGlzIHByb2ZpbGUgd2lsbCBiZSB1c2VkIHRvIGV4ZWN1dGUgRERMIHF1ZXJpZXMgLS0+XG4gICAgICAgIDwhLS0gPHByb2ZpbGU+ZGVmYXVsdDwvcHJvZmlsZT4gLS0+XG5cbiAgICAgICAgPCEtLSBDb250cm9scyBob3cgbXVjaCBPTiBDTFVTVEVSIHF1ZXJpZXMgY2FuIGJlIHJ1biBzaW11bHRhbmVvdXNseS4gLS0+XG4gICAgICAgIDwhLS0gPHBvb2xfc2l6ZT4xPC9wb29sX3NpemU+IC0tPlxuXG4gICAgICAgIDwhLS1cbiAgICAgICAgICAgIENsZWFudXAgc2V0dGluZ3MgKGFjdGl2ZSB0YXNrcyB3aWxsIG5vdCBiZSByZW1vdmVkKVxuICAgICAgICAtLT5cblxuICAgICAgICA8IS0tIENvbnRyb2xzIHRhc2sgVFRMIChkZWZhdWx0IDEgd2VlaykgLS0+XG4gICAgICAgIDwhLS0gPHRhc2tfbWF4X2xpZmV0aW1lPjYwNDgwMDwvdGFza19tYXhfbGlmZXRpbWU+IC0tPlxuXG4gICAgICAgIDwhLS0gQ29udHJvbHMgaG93IG9mdGVuIGNsZWFudXAgc2hvdWxkIGJlIHBlcmZvcm1lZCAoaW4gc2Vjb25kcykgLS0+XG4gICAgICAgIDwhLS0gPGNsZWFudXBfZGVsYXlfcGVyaW9kPjYwPC9jbGVhbnVwX2RlbGF5X3BlcmlvZD4gLS0+XG5cbiAgICAgICAgPCEtLSBDb250cm9scyBob3cgbWFueSB0YXNrcyBjb3VsZCBiZSBpbiB0aGUgcXVldWUgLS0+XG4gICAgICAgIDwhLS0gPG1heF90YXNrc19pbl9xdWV1ZT4xMDAwPC9tYXhfdGFza3NfaW5fcXVldWU+IC0tPlxuICAgIDwvZGlzdHJpYnV0ZWRfZGRsPlxuXG4gICAgPCEtLSBTZXR0aW5ncyB0byBmaW5lIHR1bmUgTWVyZ2VUcmVlIHRhYmxlcy4gU2VlIGRvY3VtZW50YXRpb24gaW4gc291cmNlIGNvZGUsIGluXG4gICAgTWVyZ2VUcmVlU2V0dGluZ3MuaCAtLT5cbiAgICA8IS0tXG4gICAgPG1lcmdlX3RyZWU+XG4gICAgICAgIDxtYXhfc3VzcGljaW91c19icm9rZW5fcGFydHM+NTwvbWF4X3N1c3BpY2lvdXNfYnJva2VuX3BhcnRzPlxuICAgIDwvbWVyZ2VfdHJlZT5cbiAgICAtLT5cblxuICAgIDwhLS0gUHJvdGVjdGlvbiBmcm9tIGFjY2lkZW50YWwgRFJPUC5cbiAgICAgICAgSWYgc2l6ZSBvZiBhIE1lcmdlVHJlZSB0YWJsZSBpcyBncmVhdGVyIHRoYW4gbWF4X3RhYmxlX3NpemVfdG9fZHJvcCAoaW4gYnl0ZXMpIHRoYW4gdGFibGUgY291bGQgbm90XG4gICAgYmUgZHJvcHBlZCB3aXRoIGFueSBEUk9QIHF1ZXJ5LlxuICAgICAgICBJZiB5b3Ugd2FudCBkbyBkZWxldGUgb25lIHRhYmxlIGFuZCBkb24ndCB3YW50IHRvIGNoYW5nZSBjbGlja2hvdXNlLXNlcnZlciBjb25maWcsIHlvdSBjb3VsZCBjcmVhdGVcbiAgICBzcGVjaWFsIGZpbGUgPGNsaWNraG91c2UtcGF0aD4vZmxhZ3MvZm9yY2VfZHJvcF90YWJsZSBhbmQgbWFrZSBEUk9QIG9uY2UuXG4gICAgICAgIEJ5IGRlZmF1bHQgbWF4X3RhYmxlX3NpemVfdG9fZHJvcCBpcyA1MEdCOyBtYXhfdGFibGVfc2l6ZV90b19kcm9wPTAgYWxsb3dzIHRvIERST1AgYW55IHRhYmxlcy5cbiAgICAgICAgVGhlIHNhbWUgZm9yIG1heF9wYXJ0aXRpb25fc2l6ZV90b19kcm9wLlxuICAgICAgICBVbmNvbW1lbnQgdG8gZGlzYWJsZSBwcm90ZWN0aW9uLlxuICAgIC0tPlxuICAgIDwhLS0gPG1heF90YWJsZV9zaXplX3RvX2Ryb3A+MDwvbWF4X3RhYmxlX3NpemVfdG9fZHJvcD4gLS0+XG4gICAgPCEtLSA8bWF4X3BhcnRpdGlvbl9zaXplX3RvX2Ryb3A+MDwvbWF4X3BhcnRpdGlvbl9zaXplX3RvX2Ryb3A+IC0tPlxuXG4gICAgPCEtLSBFeGFtcGxlIG9mIHBhcmFtZXRlcnMgZm9yIEdyYXBoaXRlTWVyZ2VUcmVlIHRhYmxlIGVuZ2luZSAtLT5cbiAgICA8Z3JhcGhpdGVfcm9sbHVwX2V4YW1wbGU+XG4gICAgICAgIDxwYXR0ZXJuPlxuICAgICAgICAgICAgPHJlZ2V4cD5jbGlja19jb3N0PC9yZWdleHA+XG4gICAgICAgICAgICA8ZnVuY3Rpb24+YW55PC9mdW5jdGlvbj5cbiAgICAgICAgICAgIDxyZXRlbnRpb24+XG4gICAgICAgICAgICAgICAgPGFnZT4wPC9hZ2U+XG4gICAgICAgICAgICAgICAgPHByZWNpc2lvbj4zNjAwPC9wcmVjaXNpb24+XG4gICAgICAgICAgICA8L3JldGVudGlvbj5cbiAgICAgICAgICAgIDxyZXRlbnRpb24+XG4gICAgICAgICAgICAgICAgPGFnZT44NjQwMDwvYWdlPlxuICAgICAgICAgICAgICAgIDxwcmVjaXNpb24+NjA8L3ByZWNpc2lvbj5cbiAgICAgICAgICAgIDwvcmV0ZW50aW9uPlxuICAgICAgICA8L3BhdHRlcm4+XG4gICAgICAgIDxkZWZhdWx0PlxuICAgICAgICAgICAgPGZ1bmN0aW9uPm1heDwvZnVuY3Rpb24+XG4gICAgICAgICAgICA8cmV0ZW50aW9uPlxuICAgICAgICAgICAgICAgIDxhZ2U+MDwvYWdlPlxuICAgICAgICAgICAgICAgIDxwcmVjaXNpb24+NjA8L3ByZWNpc2lvbj5cbiAgICAgICAgICAgIDwvcmV0ZW50aW9uPlxuICAgICAgICAgICAgPHJldGVudGlvbj5cbiAgICAgICAgICAgICAgICA8YWdlPjM2MDA8L2FnZT5cbiAgICAgICAgICAgICAgICA8cHJlY2lzaW9uPjMwMDwvcHJlY2lzaW9uPlxuICAgICAgICAgICAgPC9yZXRlbnRpb24+XG4gICAgICAgICAgICA8cmV0ZW50aW9uPlxuICAgICAgICAgICAgICAgIDxhZ2U+ODY0MDA8L2FnZT5cbiAgICAgICAgICAgICAgICA8cHJlY2lzaW9uPjM2MDA8L3ByZWNpc2lvbj5cbiAgICAgICAgICAgIDwvcmV0ZW50aW9uPlxuICAgICAgICA8L2RlZmF1bHQ+XG4gICAgPC9ncmFwaGl0ZV9yb2xsdXBfZXhhbXBsZT5cblxuICAgIDwhLS0gRGlyZWN0b3J5IGluIDxjbGlja2hvdXNlLXBhdGg+IGNvbnRhaW5pbmcgc2NoZW1hIGZpbGVzIGZvciB2YXJpb3VzIGlucHV0IGZvcm1hdHMuXG4gICAgICAgIFRoZSBkaXJlY3Rvcnkgd2lsbCBiZSBjcmVhdGVkIGlmIGl0IGRvZXNuJ3QgZXhpc3QuXG4gICAgICAtLT5cbiAgICA8Zm9ybWF0X3NjaGVtYV9wYXRoPi92YXIvbGliL2NsaWNraG91c2UvZm9ybWF0X3NjaGVtYXMvPC9mb3JtYXRfc2NoZW1hX3BhdGg+XG5cbiAgICA8IS0tIERlZmF1bHQgcXVlcnkgbWFza2luZyBydWxlcywgbWF0Y2hpbmcgbGluZXMgd291bGQgYmUgcmVwbGFjZWQgd2l0aCBzb21ldGhpbmcgZWxzZSBpbiB0aGVcbiAgICBsb2dzXG4gICAgICAgIChib3RoIHRleHQgbG9ncyBhbmQgc3lzdGVtLnF1ZXJ5X2xvZykuXG4gICAgICAgIG5hbWUgLSBuYW1lIGZvciB0aGUgcnVsZSAob3B0aW9uYWwpXG4gICAgICAgIHJlZ2V4cCAtIFJFMiBjb21wYXRpYmxlIHJlZ3VsYXIgZXhwcmVzc2lvbiAobWFuZGF0b3J5KVxuICAgICAgICByZXBsYWNlIC0gc3Vic3RpdHV0aW9uIHN0cmluZyBmb3Igc2Vuc2l0aXZlIGRhdGEgKG9wdGlvbmFsLCBieSBkZWZhdWx0IC0gc2l4IGFzdGVyaXNrcylcbiAgICAtLT5cbiAgICA8cXVlcnlfbWFza2luZ19ydWxlcz5cbiAgICAgICAgPHJ1bGU+XG4gICAgICAgICAgICA8bmFtZT5oaWRlIGVuY3J5cHQvZGVjcnlwdCBhcmd1bWVudHM8L25hbWU+XG4gICAgICAgICAgICA8cmVnZXhwPigoPzphZXNfKT8oPzplbmNyeXB0fGRlY3J5cHQpKD86X215c3FsKT8pXFxzKlxcKFxccyooPzonKD86XFxcXCd8LikrJ3wuKj8pXFxzKlxcKTwvcmVnZXhwPlxuICAgICAgICAgICAgPCEtLSBvciBtb3JlIHNlY3VyZSwgYnV0IGFsc28gbW9yZSBpbnZhc2l2ZTpcbiAgICAgICAgICAgICAgICAoYWVzX1xcdyspXFxzKlxcKC4qXFwpXG4gICAgICAgICAgICAtLT5cbiAgICAgICAgICAgIDxyZXBsYWNlPlxcMSg\/Pz8pPC9yZXBsYWNlPlxuICAgICAgICA8L3J1bGU+XG4gICAgPC9xdWVyeV9tYXNraW5nX3J1bGVzPlxuXG4gICAgPCEtLSBVbmNvbW1lbnQgdG8gdXNlIGN1c3RvbSBodHRwIGhhbmRsZXJzLlxuICAgICAgICBydWxlcyBhcmUgY2hlY2tlZCBmcm9tIHRvcCB0byBib3R0b20sIGZpcnN0IG1hdGNoIHJ1bnMgdGhlIGhhbmRsZXJcbiAgICAgICAgICAgIHVybCAtIHRvIG1hdGNoIHJlcXVlc3QgVVJMLCB5b3UgY2FuIHVzZSAncmVnZXg6JyBwcmVmaXggdG8gdXNlIHJlZ2V4IG1hdGNoKG9wdGlvbmFsKVxuICAgICAgICAgICAgbWV0aG9kcyAtIHRvIG1hdGNoIHJlcXVlc3QgbWV0aG9kLCB5b3UgY2FuIHVzZSBjb21tYXMgdG8gc2VwYXJhdGUgbXVsdGlwbGUgbWV0aG9kIG1hdGNoZXMob3B0aW9uYWwpXG4gICAgICAgICAgICBoZWFkZXJzIC0gdG8gbWF0Y2ggcmVxdWVzdCBoZWFkZXJzLCBtYXRjaCBlYWNoIGNoaWxkIGVsZW1lbnQoY2hpbGQgZWxlbWVudCBuYW1lIGlzIGhlYWRlciBuYW1lKSxcbiAgICB5b3UgY2FuIHVzZSAncmVnZXg6JyBwcmVmaXggdG8gdXNlIHJlZ2V4IG1hdGNoKG9wdGlvbmFsKVxuICAgICAgICBoYW5kbGVyIGlzIHJlcXVlc3QgaGFuZGxlclxuICAgICAgICAgICAgdHlwZSAtIHN1cHBvcnRlZCB0eXBlczogc3RhdGljLCBkeW5hbWljX3F1ZXJ5X2hhbmRsZXIsIHByZWRlZmluZWRfcXVlcnlfaGFuZGxlclxuICAgICAgICAgICAgcXVlcnkgLSB1c2Ugd2l0aCBwcmVkZWZpbmVkX3F1ZXJ5X2hhbmRsZXIgdHlwZSwgZXhlY3V0ZXMgcXVlcnkgd2hlbiB0aGUgaGFuZGxlciBpcyBjYWxsZWRcbiAgICAgICAgICAgIHF1ZXJ5X3BhcmFtX25hbWUgLSB1c2Ugd2l0aCBkeW5hbWljX3F1ZXJ5X2hhbmRsZXIgdHlwZSwgZXh0cmFjdHMgYW5kIGV4ZWN1dGVzIHRoZSB2YWx1ZVxuICAgIGNvcnJlc3BvbmRpbmcgdG8gdGhlIDxxdWVyeV9wYXJhbV9uYW1lPiB2YWx1ZSBpbiBIVFRQIHJlcXVlc3QgcGFyYW1zXG4gICAgICAgICAgICBzdGF0dXMgLSB1c2Ugd2l0aCBzdGF0aWMgdHlwZSwgcmVzcG9uc2Ugc3RhdHVzIGNvZGVcbiAgICAgICAgICAgIGNvbnRlbnRfdHlwZSAtIHVzZSB3aXRoIHN0YXRpYyB0eXBlLCByZXNwb25zZSBjb250ZW50LXR5cGVcbiAgICAgICAgICAgIHJlc3BvbnNlX2NvbnRlbnQgLSB1c2Ugd2l0aCBzdGF0aWMgdHlwZSwgUmVzcG9uc2UgY29udGVudCBzZW50IHRvIGNsaWVudCwgd2hlbiB1c2luZyB0aGUgcHJlZml4XG4gICAgJ2ZpbGU6Ly8nIG9yICdjb25maWc6Ly8nLCBmaW5kIHRoZSBjb250ZW50IGZyb20gdGhlIGZpbGUgb3IgY29uZmlndXJhdGlvbiBzZW5kIHRvIGNsaWVudC5cblxuICAgIDxodHRwX2hhbmRsZXJzPlxuICAgICAgICA8cnVsZT5cbiAgICAgICAgICAgIDx1cmw+LzwvdXJsPlxuICAgICAgICAgICAgPG1ldGhvZHM+UE9TVCxHRVQ8L21ldGhvZHM+XG4gICAgICAgICAgICA8aGVhZGVycz48cHJhZ21hPm5vLWNhY2hlPC9wcmFnbWE+PC9oZWFkZXJzPlxuICAgICAgICAgICAgPGhhbmRsZXI+XG4gICAgICAgICAgICAgICAgPHR5cGU+ZHluYW1pY19xdWVyeV9oYW5kbGVyPC90eXBlPlxuICAgICAgICAgICAgICAgIDxxdWVyeV9wYXJhbV9uYW1lPnF1ZXJ5PC9xdWVyeV9wYXJhbV9uYW1lPlxuICAgICAgICAgICAgPC9oYW5kbGVyPlxuICAgICAgICA8L3J1bGU+XG5cbiAgICAgICAgPHJ1bGU+XG4gICAgICAgICAgICA8dXJsPi9wcmVkZWZpbmVkX3F1ZXJ5PC91cmw+XG4gICAgICAgICAgICA8bWV0aG9kcz5QT1NULEdFVDwvbWV0aG9kcz5cbiAgICAgICAgICAgIDxoYW5kbGVyPlxuICAgICAgICAgICAgICAgIDx0eXBlPnByZWRlZmluZWRfcXVlcnlfaGFuZGxlcjwvdHlwZT5cbiAgICAgICAgICAgICAgICA8cXVlcnk+U0VMRUNUICogRlJPTSBzeXN0ZW0uc2V0dGluZ3M8L3F1ZXJ5PlxuICAgICAgICAgICAgPC9oYW5kbGVyPlxuICAgICAgICA8L3J1bGU+XG5cbiAgICAgICAgPHJ1bGU+XG4gICAgICAgICAgICA8aGFuZGxlcj5cbiAgICAgICAgICAgICAgICA8dHlwZT5zdGF0aWM8L3R5cGU+XG4gICAgICAgICAgICAgICAgPHN0YXR1cz4yMDA8L3N0YXR1cz5cbiAgICAgICAgICAgICAgICA8Y29udGVudF90eXBlPnRleHQvcGxhaW47IGNoYXJzZXQ9VVRGLTg8L2NvbnRlbnRfdHlwZT5cbiAgICAgICAgICAgICAgICA8cmVzcG9uc2VfY29udGVudD5jb25maWc6Ly9odHRwX3NlcnZlcl9kZWZhdWx0X3Jlc3BvbnNlPC9yZXNwb25zZV9jb250ZW50PlxuICAgICAgICAgICAgPC9oYW5kbGVyPlxuICAgICAgICA8L3J1bGU+XG4gICAgPC9odHRwX2hhbmRsZXJzPlxuICAgIC0tPlxuXG4gICAgPHNlbmRfY3Jhc2hfcmVwb3J0cz5cbiAgICAgICAgPCEtLSBDaGFuZ2luZyA8ZW5hYmxlZD4gdG8gdHJ1ZSBhbGxvd3Mgc2VuZGluZyBjcmFzaCByZXBvcnRzIHRvIC0tPlxuICAgICAgICA8IS0tIHRoZSBDbGlja0hvdXNlIGNvcmUgZGV2ZWxvcGVycyB0ZWFtIHZpYSBTZW50cnkgaHR0cHM6Ly9zZW50cnkuaW8gLS0+XG4gICAgICAgIDwhLS0gRG9pbmcgc28gYXQgbGVhc3QgaW4gcHJlLXByb2R1Y3Rpb24gZW52aXJvbm1lbnRzIGlzIGhpZ2hseSBhcHByZWNpYXRlZCAtLT5cbiAgICAgICAgPGVuYWJsZWQ+ZmFsc2U8L2VuYWJsZWQ+XG4gICAgICAgIDwhLS0gQ2hhbmdlIDxhbm9ueW1pemU+IHRvIHRydWUgaWYgeW91IGRvbid0IGZlZWwgY29tZm9ydGFibGUgYXR0YWNoaW5nIHRoZSBzZXJ2ZXIgaG9zdG5hbWVcbiAgICAgICAgdG8gdGhlIGNyYXNoIHJlcG9ydCAtLT5cbiAgICAgICAgPGFub255bWl6ZT5mYWxzZTwvYW5vbnltaXplPlxuICAgICAgICA8IS0tIERlZmF1bHQgZW5kcG9pbnQgc2hvdWxkIGJlIGNoYW5nZWQgdG8gZGlmZmVyZW50IFNlbnRyeSBEU04gb25seSBpZiB5b3UgaGF2ZSAtLT5cbiAgICAgICAgPCEtLSBzb21lIGluLWhvdXNlIGVuZ2luZWVycyBvciBoaXJlZCBjb25zdWx0YW50cyB3aG8ncmUgZ29pbmcgdG8gZGVidWcgQ2xpY2tIb3VzZSBpc3N1ZXNcbiAgICAgICAgZm9yIHlvdSAtLT5cbiAgICAgICAgPGVuZHBvaW50Pmh0dHBzOi8vNmYzMzAzNGNmZTY4NGRkN2EzYWI5ODc1ZTU3YjFjOGRAbzM4ODg3MC5pbmdlc3Quc2VudHJ5LmlvLzUyMjYyNzc8L2VuZHBvaW50PlxuICAgIDwvc2VuZF9jcmFzaF9yZXBvcnRzPlxuXG4gICAgPCEtLSBVbmNvbW1lbnQgdG8gZGlzYWJsZSBDbGlja0hvdXNlIGludGVybmFsIEROUyBjYWNoaW5nLiAtLT5cbiAgICA8IS0tIDxkaXNhYmxlX2ludGVybmFsX2Ruc19jYWNoZT4xPC9kaXNhYmxlX2ludGVybmFsX2Ruc19jYWNoZT4gLS0+XG5cbiAgICA8IS0tIFlvdSBjYW4gYWxzbyBjb25maWd1cmUgcm9ja3NkYiBsaWtlIHRoaXM6IC0tPlxuICAgIDwhLS1cbiAgICA8cm9ja3NkYj5cbiAgICAgICAgPG9wdGlvbnM+XG4gICAgICAgICAgICA8bWF4X2JhY2tncm91bmRfam9icz44PC9tYXhfYmFja2dyb3VuZF9qb2JzPlxuICAgICAgICA8L29wdGlvbnM+XG4gICAgICAgIDxjb2x1bW5fZmFtaWx5X29wdGlvbnM+XG4gICAgICAgICAgICA8bnVtX2xldmVscz4yPC9udW1fbGV2ZWxzPlxuICAgICAgICA8L2NvbHVtbl9mYW1pbHlfb3B0aW9ucz5cbiAgICAgICAgPHRhYmxlcz5cbiAgICAgICAgICAgIDx0YWJsZT5cbiAgICAgICAgICAgICAgICA8bmFtZT5UQUJMRTwvbmFtZT5cbiAgICAgICAgICAgICAgICA8b3B0aW9ucz5cbiAgICAgICAgICAgICAgICAgICAgPG1heF9iYWNrZ3JvdW5kX2pvYnM+ODwvbWF4X2JhY2tncm91bmRfam9icz5cbiAgICAgICAgICAgICAgICA8L29wdGlvbnM+XG4gICAgICAgICAgICAgICAgPGNvbHVtbl9mYW1pbHlfb3B0aW9ucz5cbiAgICAgICAgICAgICAgICAgICAgPG51bV9sZXZlbHM+MjwvbnVtX2xldmVscz5cbiAgICAgICAgICAgICAgICA8L2NvbHVtbl9mYW1pbHlfb3B0aW9ucz5cbiAgICAgICAgICAgIDwvdGFibGU+XG4gICAgICAgIDwvdGFibGVzPlxuICAgIDwvcm9ja3NkYj5cbiAgICAtLT5cbjwveWFuZGV4PiIKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vZG9ja2VyL2NsaWNraG91c2UvdXNlcnMueG1sCiAgICAgICAgdGFyZ2V0OiAvZXRjL2NsaWNraG91c2Utc2VydmVyL3VzZXJzLnhtbAogICAgICAgIGNvbnRlbnQ6ICI8P3htbCB2ZXJzaW9uPVwiMS4wXCI\/PlxuPHlhbmRleD5cbiAgICA8IS0tIFNlZSBhbHNvIHRoZSBmaWxlcyBpbiB1c2Vycy5kIGRpcmVjdG9yeSB3aGVyZSB0aGUgc2V0dGluZ3MgY2FuIGJlIG92ZXJyaWRkZW4uIC0tPlxuXG4gICAgPCEtLSBQcm9maWxlcyBvZiBzZXR0aW5ncy4gLS0+XG4gICAgPHByb2ZpbGVzPlxuICAgICAgICA8IS0tIERlZmF1bHQgc2V0dGluZ3MuIC0tPlxuICAgICAgICA8ZGVmYXVsdD5cbiAgICAgICAgICAgIDwhLS0gTWF4aW11bSBtZW1vcnkgdXNhZ2UgZm9yIHByb2Nlc3Npbmcgc2luZ2xlIHF1ZXJ5LCBpbiBieXRlcy4gLS0+XG4gICAgICAgICAgICA8bWF4X21lbW9yeV91c2FnZT4xMDAwMDAwMDAwMDwvbWF4X21lbW9yeV91c2FnZT5cblxuICAgICAgICAgICAgPCEtLSBIb3cgdG8gY2hvb3NlIGJldHdlZW4gcmVwbGljYXMgZHVyaW5nIGRpc3RyaWJ1dGVkIHF1ZXJ5IHByb2Nlc3NpbmcuXG4gICAgICAgICAgICAgICAgcmFuZG9tIC0gY2hvb3NlIHJhbmRvbSByZXBsaWNhIGZyb20gc2V0IG9mIHJlcGxpY2FzIHdpdGggbWluaW11bSBudW1iZXIgb2YgZXJyb3JzXG4gICAgICAgICAgICAgICAgbmVhcmVzdF9ob3N0bmFtZSAtIGZyb20gc2V0IG9mIHJlcGxpY2FzIHdpdGggbWluaW11bSBudW1iZXIgb2YgZXJyb3JzLCBjaG9vc2UgcmVwbGljYVxuICAgICAgICAgICAgICAgICAgd2l0aCBtaW5pbXVtIG51bWJlciBvZiBkaWZmZXJlbnQgc3ltYm9scyBiZXR3ZWVuIHJlcGxpY2EncyBob3N0bmFtZSBhbmQgbG9jYWwgaG9zdG5hbWVcbiAgICAgICAgICAgICAgICAgIChIYW1taW5nIGRpc3RhbmNlKS5cbiAgICAgICAgICAgICAgICBpbl9vcmRlciAtIGZpcnN0IGxpdmUgcmVwbGljYSBpcyBjaG9zZW4gaW4gc3BlY2lmaWVkIG9yZGVyLlxuICAgICAgICAgICAgICAgIGZpcnN0X29yX3JhbmRvbSAtIGlmIGZpcnN0IHJlcGxpY2Egb25lIGhhcyBoaWdoZXIgbnVtYmVyIG9mIGVycm9ycywgcGljayBhIHJhbmRvbSBvbmUgZnJvbSByZXBsaWNhc1xuICAgICAgICAgICAgd2l0aCBtaW5pbXVtIG51bWJlciBvZiBlcnJvcnMuXG4gICAgICAgICAgICAtLT5cbiAgICAgICAgICAgIDxsb2FkX2JhbGFuY2luZz5yYW5kb208L2xvYWRfYmFsYW5jaW5nPlxuXG4gICAgICAgICAgICA8YWxsb3dfbm9uZGV0ZXJtaW5pc3RpY19tdXRhdGlvbnM+MTwvYWxsb3dfbm9uZGV0ZXJtaW5pc3RpY19tdXRhdGlvbnM+XG5cbiAgICAgICAgPC9kZWZhdWx0PlxuXG4gICAgICAgIDwhLS0gUHJvZmlsZSB0aGF0IGFsbG93cyBvbmx5IHJlYWQgcXVlcmllcy4gLS0+XG4gICAgICAgIDxyZWFkb25seT5cbiAgICAgICAgICAgIDxyZWFkb25seT4xPC9yZWFkb25seT5cbiAgICAgICAgPC9yZWFkb25seT5cblxuICAgIDwvcHJvZmlsZXM+XG5cbiAgICA8IS0tIFVzZXJzIGFuZCBBQ0wuIC0tPlxuICAgIDx1c2Vycz5cbiAgICAgICAgPCEtLSBJZiB1c2VyIG5hbWUgd2FzIG5vdCBzcGVjaWZpZWQsICdkZWZhdWx0JyB1c2VyIGlzIHVzZWQuIC0tPlxuICAgICAgICA8ZGVmYXVsdD5cbiAgICAgICAgICAgIDwhLS0gU2VlIGFsc28gdGhlIGZpbGVzIGluIHVzZXJzLmQgZGlyZWN0b3J5IHdoZXJlIHRoZSBwYXNzd29yZCBjYW4gYmUgb3ZlcnJpZGRlbi5cblxuICAgICAgICAgICAgICAgIFBhc3N3b3JkIGNvdWxkIGJlIHNwZWNpZmllZCBpbiBwbGFpbnRleHQgb3IgaW4gU0hBMjU2IChpbiBoZXggZm9ybWF0KS5cblxuICAgICAgICAgICAgICAgIElmIHlvdSB3YW50IHRvIHNwZWNpZnkgcGFzc3dvcmQgaW4gcGxhaW50ZXh0IChub3QgcmVjb21tZW5kZWQpLCBwbGFjZSBpdCBpbiAncGFzc3dvcmQnIGVsZW1lbnQuXG4gICAgICAgICAgICAgICAgRXhhbXBsZTogPHBhc3N3b3JkPnF3ZXJ0eTwvcGFzc3dvcmQ+LlxuICAgICAgICAgICAgICAgIFBhc3N3b3JkIGNvdWxkIGJlIGVtcHR5LlxuXG4gICAgICAgICAgICAgICAgSWYgeW91IHdhbnQgdG8gc3BlY2lmeSBTSEEyNTYsIHBsYWNlIGl0IGluICdwYXNzd29yZF9zaGEyNTZfaGV4JyBlbGVtZW50LlxuICAgICAgICAgICAgICAgIEV4YW1wbGU6XG4gICAgICAgICAgICA8cGFzc3dvcmRfc2hhMjU2X2hleD42NWU4NGJlMzM1MzJmYjc4NGM0ODEyOTY3NWY5ZWZmM2E2ODJiMjcxNjhjMGVhNzQ0YjJjZjU4ZWUwMjMzN2M1PC9wYXNzd29yZF9zaGEyNTZfaGV4PlxuICAgICAgICAgICAgICAgIFJlc3RyaWN0aW9ucyBvZiBTSEEyNTY6IGltcG9zc2liaWxpdHkgdG8gY29ubmVjdCB0byBDbGlja0hvdXNlIHVzaW5nIE15U1FMIEpTIGNsaWVudCAoYXMgb2YgSnVseVxuICAgICAgICAgICAgMjAxOSkuXG5cbiAgICAgICAgICAgICAgICBJZiB5b3Ugd2FudCB0byBzcGVjaWZ5IGRvdWJsZSBTSEExLCBwbGFjZSBpdCBpbiAncGFzc3dvcmRfZG91YmxlX3NoYTFfaGV4JyBlbGVtZW50LlxuICAgICAgICAgICAgICAgIEV4YW1wbGU6XG4gICAgICAgICAgICA8cGFzc3dvcmRfZG91YmxlX3NoYTFfaGV4PmUzOTU3OTZkNjU0NmIxYjY1ZGI5ZDY2NWNkNDNmMGU4NThkZDQzMDM8L3Bhc3N3b3JkX2RvdWJsZV9zaGExX2hleD5cblxuICAgICAgICAgICAgICAgIElmIHlvdSB3YW50IHRvIHNwZWNpZnkgYSBwcmV2aW91c2x5IGRlZmluZWQgTERBUCBzZXJ2ZXIgKHNlZSAnbGRhcF9zZXJ2ZXJzJyBpbiB0aGUgbWFpbiBjb25maWcpIGZvclxuICAgICAgICAgICAgYXV0aGVudGljYXRpb24sXG4gICAgICAgICAgICAgICAgICBwbGFjZSBpdHMgbmFtZSBpbiAnc2VydmVyJyBlbGVtZW50IGluc2lkZSAnbGRhcCcgZWxlbWVudC5cbiAgICAgICAgICAgICAgICBFeGFtcGxlOiA8bGRhcD48c2VydmVyPm15X2xkYXBfc2VydmVyPC9zZXJ2ZXI+PC9sZGFwPlxuXG4gICAgICAgICAgICAgICAgSWYgeW91IHdhbnQgdG8gYXV0aGVudGljYXRlIHRoZSB1c2VyIHZpYSBLZXJiZXJvcyAoYXNzdW1pbmcgS2VyYmVyb3MgaXMgZW5hYmxlZCwgc2VlICdrZXJiZXJvcycgaW5cbiAgICAgICAgICAgIHRoZSBtYWluIGNvbmZpZyksXG4gICAgICAgICAgICAgICAgICBwbGFjZSAna2VyYmVyb3MnIGVsZW1lbnQgaW5zdGVhZCBvZiAncGFzc3dvcmQnIChhbmQgc2ltaWxhcikgZWxlbWVudHMuXG4gICAgICAgICAgICAgICAgVGhlIG5hbWUgcGFydCBvZiB0aGUgY2Fub25pY2FsIHByaW5jaXBhbCBuYW1lIG9mIHRoZSBpbml0aWF0b3IgbXVzdCBtYXRjaCB0aGUgdXNlciBuYW1lIGZvclxuICAgICAgICAgICAgYXV0aGVudGljYXRpb24gdG8gc3VjY2VlZC5cbiAgICAgICAgICAgICAgICBZb3UgY2FuIGFsc28gcGxhY2UgJ3JlYWxtJyBlbGVtZW50IGluc2lkZSAna2VyYmVyb3MnIGVsZW1lbnQgdG8gZnVydGhlciByZXN0cmljdCBhdXRoZW50aWNhdGlvbiB0b1xuICAgICAgICAgICAgb25seSB0aG9zZSByZXF1ZXN0c1xuICAgICAgICAgICAgICAgICAgd2hvc2UgaW5pdGlhdG9yJ3MgcmVhbG0gbWF0Y2hlcyBpdC5cbiAgICAgICAgICAgICAgICBFeGFtcGxlOiA8a2VyYmVyb3MgLz5cbiAgICAgICAgICAgICAgICBFeGFtcGxlOiA8a2VyYmVyb3M+PHJlYWxtPkVYQU1QTEUuQ09NPC9yZWFsbT48L2tlcmJlcm9zPlxuXG4gICAgICAgICAgICAgICAgSG93IHRvIGdlbmVyYXRlIGRlY2VudCBwYXNzd29yZDpcbiAgICAgICAgICAgICAgICBFeGVjdXRlOiBQQVNTV09SRD0kKGJhc2U2NCA8IC9kZXYvdXJhbmRvbSB8IGhlYWQgLWM4KTsgZWNobyBcIiRQQVNTV09SRFwiOyBlY2hvIC1uIFwiJFBBU1NXT1JEXCIgfFxuICAgICAgICAgICAgc2hhMjU2c3VtIHwgdHIgLWQgJy0nXG4gICAgICAgICAgICAgICAgSW4gZmlyc3QgbGluZSB3aWxsIGJlIHBhc3N3b3JkIGFuZCBpbiBzZWNvbmQgLSBjb3JyZXNwb25kaW5nIFNIQTI1Ni5cblxuICAgICAgICAgICAgICAgIEhvdyB0byBnZW5lcmF0ZSBkb3VibGUgU0hBMTpcbiAgICAgICAgICAgICAgICBFeGVjdXRlOiBQQVNTV09SRD0kKGJhc2U2NCA8IC9kZXYvdXJhbmRvbSB8IGhlYWQgLWM4KTsgZWNobyBcIiRQQVNTV09SRFwiOyBlY2hvIC1uIFwiJFBBU1NXT1JEXCIgfFxuICAgICAgICAgICAgc2hhMXN1bSB8IHRyIC1kICctJyB8IHh4ZCAtciAtcCB8IHNoYTFzdW0gfCB0ciAtZCAnLSdcbiAgICAgICAgICAgICAgICBJbiBmaXJzdCBsaW5lIHdpbGwgYmUgcGFzc3dvcmQgYW5kIGluIHNlY29uZCAtIGNvcnJlc3BvbmRpbmcgZG91YmxlIFNIQTEuXG4gICAgICAgICAgICAtLT5cbiAgICAgICAgICAgIDxwYXNzd29yZD48L3Bhc3N3b3JkPlxuXG4gICAgICAgICAgICA8IS0tIExpc3Qgb2YgbmV0d29ya3Mgd2l0aCBvcGVuIGFjY2Vzcy5cblxuICAgICAgICAgICAgICAgIFRvIG9wZW4gYWNjZXNzIGZyb20gZXZlcnl3aGVyZSwgc3BlY2lmeTpcbiAgICAgICAgICAgICAgICAgICAgPGlwPjo6LzA8L2lwPlxuXG4gICAgICAgICAgICAgICAgVG8gb3BlbiBhY2Nlc3Mgb25seSBmcm9tIGxvY2FsaG9zdCwgc3BlY2lmeTpcbiAgICAgICAgICAgICAgICAgICAgPGlwPjo6MTwvaXA+XG4gICAgICAgICAgICAgICAgICAgIDxpcD4xMjcuMC4wLjE8L2lwPlxuXG4gICAgICAgICAgICAgICAgRWFjaCBlbGVtZW50IG9mIGxpc3QgaGFzIG9uZSBvZiB0aGUgZm9sbG93aW5nIGZvcm1zOlxuICAgICAgICAgICAgICAgIDxpcD4gSVAtYWRkcmVzcyBvciBuZXR3b3JrIG1hc2suIEV4YW1wbGVzOiAyMTMuMTgwLjIwNC4zIG9yIDEwLjAuMC4xLzggb3IgMTAuMC4wLjEvMjU1LjI1NS4yNTUuMFxuICAgICAgICAgICAgICAgICAgICAyYTAyOjZiODo6MyBvciAyYTAyOjZiODo6My82NCBvciAyYTAyOjZiODo6My9mZmZmOmZmZmY6ZmZmZjpmZmZmOjouXG4gICAgICAgICAgICAgICAgPGhvc3Q+IEhvc3RuYW1lLiBFeGFtcGxlOiBzZXJ2ZXIwMS55YW5kZXgucnUuXG4gICAgICAgICAgICAgICAgICAgIFRvIGNoZWNrIGFjY2VzcywgRE5TIHF1ZXJ5IGlzIHBlcmZvcm1lZCwgYW5kIGFsbCByZWNlaXZlZCBhZGRyZXNzZXMgY29tcGFyZWQgdG8gcGVlciBhZGRyZXNzLlxuICAgICAgICAgICAgICAgIDxob3N0X3JlZ2V4cD4gUmVndWxhciBleHByZXNzaW9uIGZvciBob3N0IG5hbWVzLiBFeGFtcGxlLCBec2VydmVyXFxkXFxkLVxcZFxcZC1cXGRcXC55YW5kZXhcXC5ydSRcbiAgICAgICAgICAgICAgICAgICAgVG8gY2hlY2sgYWNjZXNzLCBETlMgUFRSIHF1ZXJ5IGlzIHBlcmZvcm1lZCBmb3IgcGVlciBhZGRyZXNzIGFuZCB0aGVuIHJlZ2V4cCBpcyBhcHBsaWVkLlxuICAgICAgICAgICAgICAgICAgICBUaGVuLCBmb3IgcmVzdWx0IG9mIFBUUiBxdWVyeSwgYW5vdGhlciBETlMgcXVlcnkgaXMgcGVyZm9ybWVkIGFuZCBhbGwgcmVjZWl2ZWQgYWRkcmVzc2VzIGNvbXBhcmVkXG4gICAgICAgICAgICB0byBwZWVyIGFkZHJlc3MuXG4gICAgICAgICAgICAgICAgICAgIFN0cm9uZ2x5IHJlY29tbWVuZGVkIHRoYXQgcmVnZXhwIGlzIGVuZHMgd2l0aCAkXG4gICAgICAgICAgICAgICAgQWxsIHJlc3VsdHMgb2YgRE5TIHJlcXVlc3RzIGFyZSBjYWNoZWQgdGlsbCBzZXJ2ZXIgcmVzdGFydC5cbiAgICAgICAgICAgIC0tPlxuICAgICAgICAgICAgPG5ldHdvcmtzPlxuICAgICAgICAgICAgICAgIDxpcD46Oi8wPC9pcD5cbiAgICAgICAgICAgIDwvbmV0d29ya3M+XG5cbiAgICAgICAgICAgIDwhLS0gU2V0dGluZ3MgcHJvZmlsZSBmb3IgdXNlci4gLS0+XG4gICAgICAgICAgICA8cHJvZmlsZT5kZWZhdWx0PC9wcm9maWxlPlxuXG4gICAgICAgICAgICA8IS0tIFF1b3RhIGZvciB1c2VyLiAtLT5cbiAgICAgICAgICAgIDxxdW90YT5kZWZhdWx0PC9xdW90YT5cblxuICAgICAgICAgICAgPCEtLSBVc2VyIGNhbiBjcmVhdGUgb3RoZXIgdXNlcnMgYW5kIGdyYW50IHJpZ2h0cyB0byB0aGVtLiAtLT5cbiAgICAgICAgICAgIDwhLS0gPGFjY2Vzc19tYW5hZ2VtZW50PjE8L2FjY2Vzc19tYW5hZ2VtZW50PiAtLT5cbiAgICAgICAgPC9kZWZhdWx0PlxuICAgIDwvdXNlcnM+XG5cbiAgICA8IS0tIFF1b3Rhcy4gLS0+XG4gICAgPHF1b3Rhcz5cbiAgICAgICAgPCEtLSBOYW1lIG9mIHF1b3RhLiAtLT5cbiAgICAgICAgPGRlZmF1bHQ+XG4gICAgICAgICAgICA8IS0tIExpbWl0cyBmb3IgdGltZSBpbnRlcnZhbC4gWW91IGNvdWxkIHNwZWNpZnkgbWFueSBpbnRlcnZhbHMgd2l0aCBkaWZmZXJlbnQgbGltaXRzLiAtLT5cbiAgICAgICAgICAgIDxpbnRlcnZhbD5cbiAgICAgICAgICAgICAgICA8IS0tIExlbmd0aCBvZiBpbnRlcnZhbC4gLS0+XG4gICAgICAgICAgICAgICAgPGR1cmF0aW9uPjM2MDA8L2R1cmF0aW9uPlxuXG4gICAgICAgICAgICAgICAgPCEtLSBObyBsaW1pdHMuIEp1c3QgY2FsY3VsYXRlIHJlc291cmNlIHVzYWdlIGZvciB0aW1lIGludGVydmFsLiAtLT5cbiAgICAgICAgICAgICAgICA8cXVlcmllcz4wPC9xdWVyaWVzPlxuICAgICAgICAgICAgICAgIDxlcnJvcnM+MDwvZXJyb3JzPlxuICAgICAgICAgICAgICAgIDxyZXN1bHRfcm93cz4wPC9yZXN1bHRfcm93cz5cbiAgICAgICAgICAgICAgICA8cmVhZF9yb3dzPjA8L3JlYWRfcm93cz5cbiAgICAgICAgICAgICAgICA8ZXhlY3V0aW9uX3RpbWU+MDwvZXhlY3V0aW9uX3RpbWU+XG4gICAgICAgICAgICA8L2ludGVydmFsPlxuICAgICAgICA8L2RlZmF1bHQ+XG4gICAgPC9xdW90YXM+XG48L3lhbmRleD5cbiIKICAgICAgLSAnY2xpY2tob3VzZS1kYXRhOi92YXIvbGliL2NsaWNraG91c2UnCiAgICBkZXBlbmRzX29uOgogICAgICAtIGthZmthCiAgICAgIC0gem9va2VlcGVyCiAgem9va2VlcGVyOgogICAgaW1hZ2U6ICd6b29rZWVwZXI6My43LjAnCiAgICB2b2x1bWVzOgogICAgICAtICd6b29rZWVwZXItZGF0YWxvZzovZGF0YWxvZycKICAgICAgLSAnem9va2VlcGVyLWRhdGE6L2RhdGEnCiAgICAgIC0gJ3pvb2tlZXBlci1sb2dzOi9sb2dzJwogIGthZmthOgogICAgaW1hZ2U6ICdnaGNyLmlvL3Bvc3Rob2cva2Fma2EtY29udGFpbmVyOnYyLjguMicKICAgIGRlcGVuZHNfb246CiAgICAgIC0gem9va2VlcGVyCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBLQUZLQV9CUk9LRVJfSUQ9MTAwMQogICAgICAtIEtBRktBX0NGR19SRVNFUlZFRF9CUk9LRVJfTUFYX0lEPTEwMDEKICAgICAgLSAnS0FGS0FfQ0ZHX0xJU1RFTkVSUz1QTEFJTlRFWFQ6Ly86OTA5MicKICAgICAgLSAnS0FGS0FfQ0ZHX0FEVkVSVElTRURfTElTVEVORVJTPVBMQUlOVEVYVDovL2thZmthOjkwOTInCiAgICAgIC0gJ0tBRktBX0NGR19aT09LRUVQRVJfQ09OTkVDVD16b29rZWVwZXI6MjE4MScKICAgICAgLSBBTExPV19QTEFJTlRFWFRfTElTVEVORVI9eWVzCiAgb2JqZWN0X3N0b3JhZ2U6CiAgICBpbWFnZTogJ21pbmlvL21pbmlvOlJFTEVBU0UuMjAyMi0wNi0yNVQxNS01MC0xNlonCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBNSU5JT19ST09UX1VTRVI9JFNFUlZJQ0VfVVNFUl9NSU5JTwogICAgICAtIE1JTklPX1JPT1RfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfTUlOSU8KICAgIGVudHJ5cG9pbnQ6IHNoCiAgICBjb21tYW5kOiAnLWMgJydta2RpciAtcCAvZGF0YS9wb3N0aG9nICYmIG1pbmlvIHNlcnZlciAtLWFkZHJlc3MgIjoxOTAwMCIgLS1jb25zb2xlLWFkZHJlc3MgIjoxOTAwMSIgL2RhdGEnJycKICAgIHZvbHVtZXM6CiAgICAgIC0gJ29iamVjdF9zdG9yYWdlOi9kYXRhJwogIG1haWxkZXY6CiAgICBpbWFnZTogJ21haWxkZXYvbWFpbGRldjoyLjAuNScKICBmbG93ZXI6CiAgICBpbWFnZTogJ21oZXIvZmxvd2VyOjIuMC4wJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIEZMT1dFUl9QT1JUOiA1NTU1CiAgICAgIENFTEVSWV9CUk9LRVJfVVJMOiAncmVkaXM6Ly9yZWRpczo2Mzc5JwogIHdlYjoKICAgIGltYWdlOiAncG9zdGhvZy9wb3N0aG9nOmxhdGVzdCcKICAgIGNvbW1hbmQ6IC9jb21wb3NlL3N0YXJ0CiAgICB2b2x1bWVzOgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9jb21wb3NlL3N0YXJ0CiAgICAgICAgdGFyZ2V0OiAvY29tcG9zZS9zdGFydAogICAgICAgIGNvbnRlbnQ6ICIjIS9iaW4vYmFzaFxuL2NvbXBvc2Uvd2FpdFxuLi9iaW4vbWlncmF0ZVxuLi9iaW4vZG9ja2VyLXNlcnZlclxuIgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9jb21wb3NlL3dhaXQKICAgICAgICB0YXJnZXQ6IC9jb21wb3NlL3dhaXQKICAgICAgICBjb250ZW50OiAiIyEvdXNyL2Jpbi9lbnYgcHl0aG9uM1xuXG5pbXBvcnQgc29ja2V0XG5pbXBvcnQgdGltZVxuXG5kZWYgbG9vcCgpOlxuICAgIHByaW50KFwiV2FpdGluZyBmb3IgQ2xpY2tIb3VzZSBhbmQgUG9zdGdyZXMgdG8gYmUgcmVhZHlcIilcbiAgICB0cnk6XG4gICAgICAgIHdpdGggc29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCwgc29ja2V0LlNPQ0tfU1RSRUFNKSBhcyBzOlxuICAgICAgICAgICAgcy5jb25uZWN0KCgnY2xpY2tob3VzZScsIDkwMDApKVxuICAgICAgICBwcmludChcIkNsaWNraG91c2UgaXMgcmVhZHlcIilcbiAgICAgICAgd2l0aCBzb2NrZXQuc29ja2V0KHNvY2tldC5BRl9JTkVULCBzb2NrZXQuU09DS19TVFJFQU0pIGFzIHM6XG4gICAgICAgICAgICBzLmNvbm5lY3QoKCdkYicsIDU0MzIpKVxuICAgICAgICBwcmludChcIlBvc3RncmVzIGlzIHJlYWR5XCIpXG4gICAgZXhjZXB0IENvbm5lY3Rpb25SZWZ1c2VkRXJyb3IgYXMgZTpcbiAgICAgICAgdGltZS5zbGVlcCg1KVxuICAgICAgICBsb29wKClcblxubG9vcCgpXG4iCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fV0VCXzgwMDAKICAgICAgLSBPUFRfT1VUX0NBUFRVUklORz10cnVlCiAgICAgIC0gRElTQUJMRV9TRUNVUkVfU1NMX1JFRElSRUNUPXRydWUKICAgICAgLSBJU19CRUhJTkRfUFJPWFk9dHJ1ZQogICAgICAtIFRSVVNUX0FMTF9QUk9YSUVTPXRydWUKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vcG9zdGhvZzokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0BkYjo1NDMyL3Bvc3Rob2cnCiAgICAgIC0gQ0xJQ0tIT1VTRV9IT1NUPWNsaWNraG91c2UKICAgICAgLSBDTElDS0hPVVNFX0RBVEFCQVNFPXBvc3Rob2cKICAgICAgLSBDTElDS0hPVVNFX1NFQ1VSRT1mYWxzZQogICAgICAtIENMSUNLSE9VU0VfVkVSSUZZPWZhbHNlCiAgICAgIC0gS0FGS0FfSE9TVFM9a2Fma2EKICAgICAgLSAnUkVESVNfVVJMPXJlZGlzOi8vcmVkaXM6NjM3OS8nCiAgICAgIC0gUEdIT1NUPWRiCiAgICAgIC0gUEdVU0VSPXBvc3Rob2cKICAgICAgLSBQR1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gREVQTE9ZTUVOVD1ob2JieQogICAgICAtIFNJVEVfVVJMPSRTRVJWSUNFX0ZRRE5fV0VCCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfU0VDUkVUS0VZCiAgICBkZXBlbmRzX29uOgogICAgICAtIGRiCiAgICAgIC0gcmVkaXMKICAgICAgLSBjbGlja2hvdXNlCiAgICAgIC0ga2Fma2EKICAgICAgLSBvYmplY3Rfc3RvcmFnZQogIHdvcmtlcjoKICAgIGltYWdlOiAncG9zdGhvZy9wb3N0aG9nOmxhdGVzdCcKICAgIGNvbW1hbmQ6ICcuL2Jpbi9kb2NrZXItd29ya2VyLWNlbGVyeSAtLXdpdGgtc2NoZWR1bGVyJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gT1BUX09VVF9DQVBUVVJJTkc9dHJ1ZQogICAgICAtIERJU0FCTEVfU0VDVVJFX1NTTF9SRURJUkVDVD10cnVlCiAgICAgIC0gSVNfQkVISU5EX1BST1hZPXRydWUKICAgICAgLSBUUlVTVF9BTExfUFJPWElFUz10cnVlCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovL3Bvc3Rob2c6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNAZGI6NTQzMi9wb3N0aG9nJwogICAgICAtIENMSUNLSE9VU0VfSE9TVD1jbGlja2hvdXNlCiAgICAgIC0gQ0xJQ0tIT1VTRV9EQVRBQkFTRT1wb3N0aG9nCiAgICAgIC0gQ0xJQ0tIT1VTRV9TRUNVUkU9ZmFsc2UKICAgICAgLSBDTElDS0hPVVNFX1ZFUklGWT1mYWxzZQogICAgICAtIEtBRktBX0hPU1RTPWthZmthCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovL3JlZGlzOjYzNzkvJwogICAgICAtIFBHSE9TVD1kYgogICAgICAtIFBHVVNFUj1wb3N0aG9nCiAgICAgIC0gUEdQQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtIERFUExPWU1FTlQ9aG9iYnkKICAgICAgLSBTSVRFX1VSTD0kU0VSVklDRV9GUUROX1dFQgogICAgICAtIFNFQ1JFVF9LRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X1NFQ1JFVEtFWQogICAgZGVwZW5kc19vbjoKICAgICAgLSBkYgogICAgICAtIHJlZGlzCiAgICAgIC0gY2xpY2tob3VzZQogICAgICAtIGthZmthCiAgICAgIC0gb2JqZWN0X3N0b3JhZ2UKICBwbHVnaW5zOgogICAgaW1hZ2U6ICdwb3N0aG9nL3Bvc3Rob2c6bGF0ZXN0JwogICAgY29tbWFuZDogJy4vYmluL3BsdWdpbi1zZXJ2ZXIgLS1uby1yZXN0YXJ0LWxvb3AnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vcG9zdGhvZzokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0BkYjo1NDMyL3Bvc3Rob2cnCiAgICAgIC0gJ0tBRktBX0hPU1RTPWthZmthOjkwOTInCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovL3JlZGlzOjYzNzkvJwogICAgICAtIENMSUNLSE9VU0VfSE9TVD1jbGlja2hvdXNlCiAgICAgIC0gQ0xJQ0tIT1VTRV9EQVRBQkFTRT1wb3N0aG9nCiAgICAgIC0gQ0xJQ0tIT1VTRV9TRUNVUkU9ZmFsc2UKICAgICAgLSBDTElDS0hPVVNFX1ZFUklGWT1mYWxzZQogICAgICAtIFNJVEVfVVJMPSRTRVJWSUNFX0ZRRE5fV0VCCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfU0VDUkVUS0VZCiAgICBkZXBlbmRzX29uOgogICAgICAtIGRiCiAgICAgIC0gcmVkaXMKICAgICAgLSBjbGlja2hvdXNlCiAgICAgIC0ga2Fma2EKICAgICAgLSBvYmplY3Rfc3RvcmFnZQogIGVsYXN0aWNzZWFyY2g6CiAgICBpbWFnZTogJ2VsYXN0aWNzZWFyY2g6Ny4xNi4yJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gY2x1c3Rlci5yb3V0aW5nLmFsbG9jYXRpb24uZGlzay50aHJlc2hvbGRfZW5hYmxlZD10cnVlCiAgICAgIC0gY2x1c3Rlci5yb3V0aW5nLmFsbG9jYXRpb24uZGlzay53YXRlcm1hcmsubG93PTUxMm1iCiAgICAgIC0gY2x1c3Rlci5yb3V0aW5nLmFsbG9jYXRpb24uZGlzay53YXRlcm1hcmsuaGlnaD0yNTZtYgogICAgICAtIGNsdXN0ZXIucm91dGluZy5hbGxvY2F0aW9uLmRpc2sud2F0ZXJtYXJrLmZsb29kX3N0YWdlPTEyOG1iCiAgICAgIC0gZGlzY292ZXJ5LnR5cGU9c2luZ2xlLW5vZGUKICAgICAgLSAnRVNfSkFWQV9PUFRTPS1YbXMyNTZtIC1YbXgyNTZtJwogICAgICAtIHhwYWNrLnNlY3VyaXR5LmVuYWJsZWQ9ZmFsc2UKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2VsYXN0aWNzZWFyY2gtZGF0YTovdmFyL2xpYi9lbGFzdGljc2VhcmNoL2RhdGEnCiAgdGVtcG9yYWw6CiAgICBpbWFnZTogJ3RlbXBvcmFsaW8vYXV0by1zZXR1cDoxLjIwLjAnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBEQj1wb3N0Z3Jlc3FsCiAgICAgIC0gREJfUE9SVD01NDMyCiAgICAgIC0gUE9TVEdSRVNfVVNFUj1wb3N0aG9nCiAgICAgIC0gUE9TVEdSRVNfUFdEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfU0VFRFM9ZGIKICAgICAgLSBEWU5BTUlDX0NPTkZJR19GSUxFX1BBVEg9Y29uZmlnL2R5bmFtaWNjb25maWcvZGV2ZWxvcG1lbnQtc3FsLnlhbWwKICAgICAgLSBFTkFCTEVfRVM9dHJ1ZQogICAgICAtIEVTX1NFRURTPWVsYXN0aWNzZWFyY2gKICAgICAgLSBFU19WRVJTSU9OPXY3CiAgICAgIC0gRU5BQkxFX0VTPWZhbHNlCiAgICBkZXBlbmRzX29uOgogICAgICBkYjoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgdm9sdW1lczoKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vZG9ja2VyL3RlbXBvcmFsL2R5bmFtaWNjb25maWcvZGV2ZWxvcG1lbnQtc3FsLnlhbWwKICAgICAgICB0YXJnZXQ6IC9ldGMvdGVtcG9yYWwvY29uZmlnL2R5bmFtaWNjb25maWcvZGV2ZWxvcG1lbnQtc3FsLnlhbWwKICAgICAgICBjb250ZW50OiAibGltaXQubWF4SURMZW5ndGg6XG4gICAgLSB2YWx1ZTogMjU1XG4gICAgICBjb25zdHJhaW50czoge31cbnN5c3RlbS5mb3JjZVNlYXJjaEF0dHJpYnV0ZXNDYWNoZVJlZnJlc2hPblJlYWQ6XG4gICAgLSB2YWx1ZTogZmFsc2VcbiAgICAgIGNvbnN0cmFpbnRzOiB7fVxuIgogIHRlbXBvcmFsLWFkbWluLXRvb2xzOgogICAgaW1hZ2U6ICd0ZW1wb3JhbGlvL2FkbWluLXRvb2xzOjEuMjAuMCcKICAgIGRlcGVuZHNfb246CiAgICAgIC0gdGVtcG9yYWwKICAgIGVudmlyb25tZW50OgogICAgICAtICdURU1QT1JBTF9DTElfQUREUkVTUz10ZW1wb3JhbDo3MjMzJwogICAgc3RkaW5fb3BlbjogdHJ1ZQogICAgdHR5OiB0cnVlCiAgdGVtcG9yYWwtdWk6CiAgICBpbWFnZTogJ3RlbXBvcmFsaW8vdWk6Mi4xMC4zJwogICAgZGVwZW5kc19vbjoKICAgICAgLSB0ZW1wb3JhbAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1RFTVBPUkFMX0FERFJFU1M9dGVtcG9yYWw6NzIzMycKICAgICAgLSAnVEVNUE9SQUxfQ09SU19PUklHSU5TPWh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCcKICB0ZW1wb3JhbC1kamFuZ28td29ya2VyOgogICAgaW1hZ2U6ICdwb3N0aG9nL3Bvc3Rob2c6bGF0ZXN0JwogICAgY29tbWFuZDogLi9iaW4vdGVtcG9yYWwtZGphbmdvLXdvcmtlcgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gRElTQUJMRV9TRUNVUkVfU1NMX1JFRElSRUNUPXRydWUKICAgICAgLSBJU19CRUhJTkRfUFJPWFk9dHJ1ZQogICAgICAtIFRSVVNUX0FMTF9QUk9YSUVTPXRydWUKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vcG9zdGhvZzokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0BkYjo1NDMyL3Bvc3Rob2cnCiAgICAgIC0gQ0xJQ0tIT1VTRV9IT1NUPWNsaWNraG91c2UKICAgICAgLSBDTElDS0hPVVNFX0RBVEFCQVNFPXBvc3Rob2cKICAgICAgLSBDTElDS0hPVVNFX1NFQ1VSRT1mYWxzZQogICAgICAtIENMSUNLSE9VU0VfVkVSSUZZPWZhbHNlCiAgICAgIC0gS0FGS0FfSE9TVFM9a2Fma2EKICAgICAgLSAnUkVESVNfVVJMPXJlZGlzOi8vcmVkaXM6NjM3OS8nCiAgICAgIC0gUEdIT1NUPWRiCiAgICAgIC0gUEdVU0VSPXBvc3Rob2cKICAgICAgLSBQR1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gREVQTE9ZTUVOVD1ob2JieQogICAgICAtIFNJVEVfVVJMPSRTRVJWSUNFX0ZRRE5fV0VCCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfU0VDUkVUS0VZCiAgICAgIC0gVEVNUE9SQUxfSE9TVD10ZW1wb3JhbAogICAgZGVwZW5kc19vbjoKICAgICAgLSBkYgogICAgICAtIHJlZGlzCiAgICAgIC0gY2xpY2tob3VzZQogICAgICAtIGthZmthCiAgICAgIC0gb2JqZWN0X3N0b3JhZ2UKICAgICAgLSB0ZW1wb3JhbAo=","tags":["analytics","product","open-source","self-hosted","ab-testing","event-tracking"],"logo":"svgs\/posthog.svg","minversion":"4.0.0-beta.222"},"reactive-resume":{"documentation":"https:\/\/rxresu.me\/?utm_source=coolify.io","slogan":"A one-of-a-kind resume builder that keeps your privacy in mind.","compose":"c2VydmljZXM6CiAgcmVhY3RpdmUtcmVzdW1lOgogICAgaW1hZ2U6ICdhbXJ1dGhwaWxsYWkvcmVhY3RpdmUtcmVzdW1lOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9SRUFDVElWRVJFU1VNRV8zMDAwCiAgICAgIC0gUFVCTElDX1VSTD0kU0VSVklDRV9GUUROX1JFQUNUSVZFUkVTVU1FCiAgICAgIC0gJ1NUT1JBR0VfVVJMPWh0dHA6Ly9taW5pbycKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzcWw6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU19EQjotcG9zdGdyZXN9JwogICAgICAtIEFDQ0VTU19UT0tFTl9TRUNSRVQ9JFNFUlZJQ0VfUEFTU1dPUkRfQUNDRVNTVE9LRU4KICAgICAgLSBSRUZSRVNIX1RPS0VOX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF9SRUZSRVNIVE9LRU4KICAgICAgLSBDSFJPTUVfVE9LRU49JFNFUlZJQ0VfUEFTU1dPUkRfQ0hST01FVE9LRU4KICAgICAgLSAnQ0hST01FX1VSTD13czovL2Nocm9tZTozMDAwJwogICAgICAtICdSRURJU19VUkw9cmVkaXM6Ly9yZWRpczo2Mzc5JwogICAgICAtIFNUT1JBR0VfRU5EUE9JTlQ9bWluaW8KICAgICAgLSBTVE9SQUdFX1BPUlQ9OTAwMAogICAgICAtIFNUT1JBR0VfUkVHSU9OPXVzLWVhc3QtMQogICAgICAtIFNUT1JBR0VfQlVDS0VUPWRlZmF1bHQKICAgICAgLSBTVE9SQUdFX0FDQ0VTU19LRVk9JFNFUlZJQ0VfVVNFUl9NSU5JTwogICAgICAtIFNUT1JBR0VfU0VDUkVUX0tFWT0kU0VSVklDRV9QQVNTV09SRF9NSU5JTwogICAgICAtIFNUT1JBR0VfVVNFX1NTTD1mYWxzZQogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3JlcwogICAgICAtIG1pbmlvCiAgICAgIC0gY2hyb21lCiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1wb3N0Z3Jlc30nCiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIG1pbmlvOgogICAgaW1hZ2U6ICdxdWF5LmlvL21pbmlvL21pbmlvOmxhdGVzdCcKICAgIGNvbW1hbmQ6ICdzZXJ2ZXIgL2RhdGEgLS1jb25zb2xlLWFkZHJlc3MgIjo5MDAxIicKICAgIGVudmlyb25tZW50OgogICAgICAtIE1JTklPX1JPT1RfVVNFUj0kU0VSVklDRV9VU0VSX01JTklPCiAgICAgIC0gTUlOSU9fUk9PVF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9NSU5JTwogICAgdm9sdW1lczoKICAgICAgLSAnbWluaW8tZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo5MDAwL21pbmlvL2hlYWx0aC9saXZlJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgY2hyb21lOgogICAgaW1hZ2U6ICdnaGNyLmlvL2Jyb3dzZXJsZXNzL2Nocm9tZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBIRUFMVEg9dHJ1ZQogICAgICAtIFRJTUVPVVQ9MTAwMDAKICAgICAgLSBDT05DVVJSRU5UPTEwCiAgICAgIC0gVE9LRU49JFNFUlZJQ0VfUEFTU1dPUkRfQ0hST01FVE9LRU4KICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6YWxwaW5lJwogICAgY29tbWFuZDogcmVkaXMtc2VydmVyCiAgICB2b2x1bWVzOgogICAgICAtICdyZWRpc19kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["reactive-resume","resume-builder","open-source","2fa"],"logo":"svgs\/rxresume.svg","minversion":"0.0.0","port":"3000"},"rocketchat":{"documentation":"https:\/\/github.com\/RocketChat\/Rocket.Chat?utm_source=coolify.io","slogan":"Self-hosted, secure and highly customizable open-source communication platform for organizations with sophisticated security and privacy concerns.","compose":"c2VydmljZXM6CiAgcm9ja2V0Y2hhdDoKICAgIGltYWdlOiAncmVnaXN0cnkucm9ja2V0LmNoYXQvcm9ja2V0Y2hhdC9yb2NrZXQuY2hhdDpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fUk9DS0VUQ0hBVF8zMDAwCiAgICAgIC0gJ01PTkdPX1VSTD1tb25nb2RiOi8vJHtNT05HT0RCX0FEVkVSVElTRURfSE9TVE5BTUU6LW1vbmdvZGJ9OiR7TU9OR09EQl9JTklUSUFMX1BSSU1BUllfUE9SVF9OVU1CRVI6LTI3MDE3fS8ke01PTkdPREJfREFUQUJBU0U6LXJvY2tldGNoYXR9P3JlcGxpY2FTZXQ9JHtNT05HT0RCX1JFUExJQ0FfU0VUX05BTUU6LXJzMH0nCiAgICAgIC0gJ01PTkdPX09QTE9HX1VSTD1tb25nb2RiOi8vJHtNT05HT0RCX0FEVkVSVElTRURfSE9TVE5BTUU6LW1vbmdvZGJ9OiR7TU9OR09EQl9JTklUSUFMX1BSSU1BUllfUE9SVF9OVU1CRVI6LTI3MDE3fS9sb2NhbD9yZXBsaWNhU2V0PSR7TU9OR09EQl9SRVBMSUNBX1NFVF9OQU1FOi1yczB9JwogICAgICAtIFJPT1RfVVJMPSRTRVJWSUNFX0ZRRE5fUk9DS0VUQ0hBVAogICAgICAtIERFUExPWV9NRVRIT0Q9ZG9ja2VyCiAgICAgIC0gUkVHX1RPS0VOPSRSRUdfVE9LRU4KICAgIGRlcGVuZHNfb246CiAgICAgIG1vbmdvZGI6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBub2RlCiAgICAgICAgLSAnLS1ldmFsJwogICAgICAgIC0gImNvbnN0IGh0dHAgPSByZXF1aXJlKCdodHRwJyk7IGNvbnN0IG9wdGlvbnMgPSB7IGhvc3Q6ICcwLjAuMC4wJywgcG9ydDogMzAwMCwgdGltZW91dDogMjAwMCwgcGF0aDogJy9oZWFsdGgnIH07IGNvbnN0IGhlYWx0aENoZWNrID0gaHR0cC5yZXF1ZXN0KG9wdGlvbnMsIChyZXMpID0+IHsgY29uc29sZS5sb2coJ0hFQUxUSENIRUNLIFNUQVRVUzonLCByZXMuc3RhdHVzQ29kZSk7IGlmIChyZXMuc3RhdHVzQ29kZSA9PSAyMDApIHsgcHJvY2Vzcy5leGl0KDApOyB9IGVsc2UgeyBwcm9jZXNzLmV4aXQoMSk7IH0gfSk7IGhlYWx0aENoZWNrLm9uKCdlcnJvcicsIGZ1bmN0aW9uIChlcnIpIHsgY29uc29sZS5lcnJvcignRVJST1InKTsgcHJvY2Vzcy5leGl0KDEpOyB9KTsgaGVhbHRoQ2hlY2suZW5kKCk7IgogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgbW9uZ29kYjoKICAgIGltYWdlOiAnZG9ja2VyLmlvL2JpdG5hbWkvbW9uZ29kYjo1LjAnCiAgICB2b2x1bWVzOgogICAgICAtICdtb25nb2RiX2RhdGE6L2JpdG5hbWkvbW9uZ29kYicKICAgIGVudmlyb25tZW50OgogICAgICAtIE1PTkdPREJfUkVQTElDQV9TRVRfTU9ERT1wcmltYXJ5CiAgICAgIC0gJ01PTkdPREJfUkVQTElDQV9TRVRfTkFNRT0ke01PTkdPREJfUkVQTElDQV9TRVRfTkFNRTotcnMwfScKICAgICAgLSAnTU9OR09EQl9QT1JUX05VTUJFUj0ke01PTkdPREJfUE9SVF9OVU1CRVI6LTI3MDE3fScKICAgICAgLSAnTU9OR09EQl9JTklUSUFMX1BSSU1BUllfSE9TVD0ke01PTkdPREJfSU5JVElBTF9QUklNQVJZX0hPU1Q6LW1vbmdvZGJ9JwogICAgICAtICdNT05HT0RCX0lOSVRJQUxfUFJJTUFSWV9QT1JUX05VTUJFUj0ke01PTkdPREJfSU5JVElBTF9QUklNQVJZX1BPUlRfTlVNQkVSOi0yNzAxN30nCiAgICAgIC0gJ01PTkdPREJfQURWRVJUSVNFRF9IT1NUTkFNRT0ke01PTkdPREJfQURWRVJUSVNFRF9IT1NUTkFNRTotbW9uZ29kYn0nCiAgICAgIC0gJ01PTkdPREJfRU5BQkxFX0pPVVJOQUw9JHtNT05HT0RCX0VOQUJMRV9KT1VSTkFMOi10cnVlfScKICAgICAgLSAnQUxMT1dfRU1QVFlfUEFTU1dPUkQ9JHtBTExPV19FTVBUWV9QQVNTV09SRDoteWVzfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAiZWNobyAnZGIuc3RhdHMoKS5vaycgfCBtb25nbyBsb2NhbGhvc3Q6MjcwMTcvdGVzdCAtLXF1aWV0IgogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==","tags":["rocketchat","chat","communication","privacy","mongodb","open","source"],"logo":"svgs\/rocketchat.svg","minversion":"0.0.0","port":"3000"},"shlink":{"documentation":"https:\/\/shlink.io\/?utm_source=coolify.io","slogan":"The definitive self-hosted URL shortener","compose":"c2VydmljZXM6CiAgc2hsaW5rOgogICAgaW1hZ2U6ICdzaGxpbmtpby9zaGxpbms6c3RhYmxlJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1NITElOS184MDgwCiAgICAgIC0gJ0RFRkFVTFRfRE9NQUlOPSR7U0VSVklDRV9VUkxfU0hMSU5LfScKICAgICAgLSBJU19IVFRQU19FTkFCTEVEPWZhbHNlCiAgICAgIC0gJ0lOSVRJQUxfQVBJX0tFWT0ke1NFUlZJQ0VfQkFTRTY0X1NITElOS0FQSUtFWX0nCiAgICB2b2x1bWVzOgogICAgICAtICdzaGxpbmstZGF0YTovZXRjL3NobGluay9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwODAvcmVzdC92My9oZWFsdGgnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICBzaGxpbmstd2ViOgogICAgaW1hZ2U6IHNobGlua2lvL3NobGluay13ZWItY2xpZW50CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fU0hMSU5LV0VCXzgwODAKICAgICAgLSAnU0hMSU5LX1NFUlZFUl9BUElfS0VZPSR7U0VSVklDRV9CQVNFNjRfU0hMSU5LQVBJS0VZfScKICAgICAgLSAnU0hMSU5LX1NFUlZFUl9VUkw9JHtTRVJWSUNFX0ZRRE5fU0hMSU5LfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MDgwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==","tags":["links","shortener","sharing","url","short","link","sharing"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"8080"},"slash":{"documentation":"https:\/\/github.com\/yourselfhosted\/slash?utm_source=coolify.io","slogan":"An open source, self-hosted links shortener and sharing platform.","compose":"c2VydmljZXM6CiAgc2xhc2g6CiAgICBpbWFnZTogeW91cnNlbGZob3N0ZWQvc2xhc2gKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9TTEFTSF81MjMxCiAgICB2b2x1bWVzOgogICAgICAtICdzbGFzaC1kYXRhOi92YXIvb3B0L3NsYXNoJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHdnZXQKICAgICAgICAtICctcScKICAgICAgICAtICctLXNwaWRlcicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjUyMzEnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["links","shortener","sharing","url","short","link","sharing"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"5231"},"snapdrop":{"documentation":"https:\/\/github.com\/RobinLinus\/snapdrop?utm_source=coolify.io","slogan":"A self-hosted file-sharing service for secure and convenient file transfers, whether on a local network or the internet.","compose":"c2VydmljZXM6CiAgc25hcGRyb3A6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvc25hcGRyb3A6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1NOQVBEUk9QCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnc25hcGRyb3AtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["file","sharing","transfer","local","network","internet"],"logo":"svgs\/unknown.svg","minversion":"0.0.0"},"statusnook":{"documentation":"https:\/\/statusnook.com?utm_source=coolify.io","slogan":"Effortlessly deploy a status page and start monitoring endpoints in minutes","compose":"c2VydmljZXM6CiAgc3RhdHVzbm9vazoKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9TVEFUVVNOT09LXzgwMDAKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3N0YXR1c25vb2stZGF0YTovYXBwL3N0YXR1c25vb2stZGF0YScKICAgIGltYWdlOiBnb2tzYW4vc3RhdHVzbm9vawogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHdnZXQKICAgICAgICAtICctcScKICAgICAgICAtICctLXNwaWRlcicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["go","html","monitoring","sqlite","self","hosted","status","page","htmx","smtp","slack"],"logo":"svgs\/statusnook.svg","minversion":"0.0.0","port":"8000"},"stirling-pdf":{"documentation":"https:\/\/github.com\/Stirling-Tools\/Stirling-PDF?utm_source=coolify.io","slogan":"Stirling is a powerful web based PDF manipulation tool","compose":"c2VydmljZXM6CiAgc3RpcmxpbmctcGRmOgogICAgaW1hZ2U6ICdmcm9vb2RsZS9zLXBkZjpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdzdGlybGluZy10cmFpbmluZy1kYXRhOi91c3Ivc2hhcmUvdGVzc2VyYWN0LW9jci81L3Rlc3NkYXRhJwogICAgICAtICdzdGlybGluZy1jb25maWdzOi9jb25maWdzJwogICAgICAtICdzdGlybGluZy1jdXN0b20tZmlsZXM6L2N1c3RvbUZpbGVzLycKICAgICAgLSAnc3RpcmxpbmctbG9nczovbG9ncy8nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fU1BERl84MDgwCiAgICAgIC0gRE9DS0VSX0VOQUJMRV9TRUNVUklUWT1mYWxzZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6ICdjdXJsIC0tZmFpbCAtSSBodHRwOi8vMTI3LjAuMC4xOjgwODAgfHwgZXhpdCAxJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["pdf","manipulation","web","tool"],"logo":"svgs\/stirling.png","minversion":"0.0.0","port":"8080"},"supabase":{"documentation":"https:\/\/supabase.io?utm_source=coolify.io","slogan":"The open source Firebase alternative.","compose":"c2VydmljZXM6CiAgc3VwYWJhc2Uta29uZzoKICAgIGltYWdlOiAna29uZzoyLjguMScKICAgIGVudHJ5cG9pbnQ6ICdiYXNoIC1jICcnZXZhbCAiZWNobyBcIiQkKGNhdCB+L3RlbXAueW1sKVwiIiA+IH4va29uZy55bWwgJiYgL2RvY2tlci1lbnRyeXBvaW50LnNoIGtvbmcgZG9ja2VyLXN0YXJ0JycnCiAgICBkZXBlbmRzX29uOgogICAgICBzdXBhYmFzZS1hbmFseXRpY3M6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9TVVBBQkFTRUtPTkcKICAgICAgLSAnSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfScKICAgICAgLSBLT05HX0RBVEFCQVNFPW9mZgogICAgICAtIEtPTkdfREVDTEFSQVRJVkVfQ09ORklHPS9ob21lL2tvbmcva29uZy55bWwKICAgICAgLSAnS09OR19ETlNfT1JERVI9TEFTVCxBLENOQU1FJwogICAgICAtICdLT05HX1BMVUdJTlM9cmVxdWVzdC10cmFuc2Zvcm1lcixjb3JzLGtleS1hdXRoLGFjbCxiYXNpYy1hdXRoJwogICAgICAtIEtPTkdfTkdJTlhfUFJPWFlfUFJPWFlfQlVGRkVSX1NJWkU9MTYwawogICAgICAtICdLT05HX05HSU5YX1BST1hZX1BST1hZX0JVRkZFUlM9NjQgMTYwaycKICAgICAgLSAnU1VQQUJBU0VfQU5PTl9LRVk9JHtTRVJWSUNFX1NVUEFCQVNFQU5PTl9LRVl9JwogICAgICAtICdTVVBBQkFTRV9TRVJWSUNFX0tFWT0ke1NFUlZJQ0VfU1VQQUJBU0VTRVJWSUNFX0tFWX0nCiAgICAgIC0gJ0RBU0hCT0FSRF9VU0VSTkFNRT0ke1NFUlZJQ0VfVVNFUl9BRE1JTn0nCiAgICAgIC0gJ0RBU0hCT0FSRF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfQURNSU59JwogICAgdm9sdW1lczoKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vdm9sdW1lcy9hcGkva29uZy55bWwKICAgICAgICB0YXJnZXQ6IC9ob21lL2tvbmcvdGVtcC55bWwKICAgICAgICBjb250ZW50OiAiX2Zvcm1hdF92ZXJzaW9uOiAnMi4xJ1xuX3RyYW5zZm9ybTogdHJ1ZVxuXG4jIyNcbiMjIyBDb25zdW1lcnMgLyBVc2Vyc1xuIyMjXG5jb25zdW1lcnM6XG4gIC0gdXNlcm5hbWU6IERBU0hCT0FSRFxuICAtIHVzZXJuYW1lOiBhbm9uXG4gICAga2V5YXV0aF9jcmVkZW50aWFsczpcbiAgICAgIC0ga2V5OiAkU1VQQUJBU0VfQU5PTl9LRVlcbiAgLSB1c2VybmFtZTogc2VydmljZV9yb2xlXG4gICAga2V5YXV0aF9jcmVkZW50aWFsczpcbiAgICAgIC0ga2V5OiAkU1VQQUJBU0VfU0VSVklDRV9LRVlcblxuIyMjXG4jIyMgQWNjZXNzIENvbnRyb2wgTGlzdFxuIyMjXG5hY2xzOlxuICAtIGNvbnN1bWVyOiBhbm9uXG4gICAgZ3JvdXA6IGFub25cbiAgLSBjb25zdW1lcjogc2VydmljZV9yb2xlXG4gICAgZ3JvdXA6IGFkbWluXG5cbiMjI1xuIyMjIERhc2hib2FyZCBjcmVkZW50aWFsc1xuIyMjXG5iYXNpY2F1dGhfY3JlZGVudGlhbHM6XG4tIGNvbnN1bWVyOiBEQVNIQk9BUkRcbiAgdXNlcm5hbWU6ICREQVNIQk9BUkRfVVNFUk5BTUVcbiAgcGFzc3dvcmQ6ICREQVNIQk9BUkRfUEFTU1dPUkRcblxuXG4jIyNcbiMjIyBBUEkgUm91dGVzXG4jIyNcbnNlcnZpY2VzOlxuXG4gICMjIE9wZW4gQXV0aCByb3V0ZXNcbiAgLSBuYW1lOiBhdXRoLXYxLW9wZW5cbiAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1hdXRoOjk5OTkvdmVyaWZ5XG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiBhdXRoLXYxLW9wZW5cbiAgICAgICAgc3RyaXBfcGF0aDogdHJ1ZVxuICAgICAgICBwYXRoczpcbiAgICAgICAgICAtIC9hdXRoL3YxL3ZlcmlmeVxuICAgIHBsdWdpbnM6XG4gICAgICAtIG5hbWU6IGNvcnNcbiAgLSBuYW1lOiBhdXRoLXYxLW9wZW4tY2FsbGJhY2tcbiAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1hdXRoOjk5OTkvY2FsbGJhY2tcbiAgICByb3V0ZXM6XG4gICAgICAtIG5hbWU6IGF1dGgtdjEtb3Blbi1jYWxsYmFja1xuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL2F1dGgvdjEvY2FsbGJhY2tcbiAgICBwbHVnaW5zOlxuICAgICAgLSBuYW1lOiBjb3JzXG4gIC0gbmFtZTogYXV0aC12MS1vcGVuLWF1dGhvcml6ZVxuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLWF1dGg6OTk5OS9hdXRob3JpemVcbiAgICByb3V0ZXM6XG4gICAgICAtIG5hbWU6IGF1dGgtdjEtb3Blbi1hdXRob3JpemVcbiAgICAgICAgc3RyaXBfcGF0aDogdHJ1ZVxuICAgICAgICBwYXRoczpcbiAgICAgICAgICAtIC9hdXRoL3YxL2F1dGhvcml6ZVxuICAgIHBsdWdpbnM6XG4gICAgICAtIG5hbWU6IGNvcnNcblxuICAjIyBTZWN1cmUgQXV0aCByb3V0ZXNcbiAgLSBuYW1lOiBhdXRoLXYxXG4gICAgX2NvbW1lbnQ6ICdHb1RydWU6IC9hdXRoL3YxLyogLT4gaHR0cDovL3N1cGFiYXNlLWF1dGg6OTk5OS8qJ1xuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLWF1dGg6OTk5OS9cbiAgICByb3V0ZXM6XG4gICAgICAtIG5hbWU6IGF1dGgtdjEtYWxsXG4gICAgICAgIHN0cmlwX3BhdGg6IHRydWVcbiAgICAgICAgcGF0aHM6XG4gICAgICAgICAgLSAvYXV0aC92MS9cbiAgICBwbHVnaW5zOlxuICAgICAgLSBuYW1lOiBjb3JzXG4gICAgICAtIG5hbWU6IGtleS1hdXRoXG4gICAgICAgIGNvbmZpZzpcbiAgICAgICAgICBoaWRlX2NyZWRlbnRpYWxzOiBmYWxzZVxuICAgICAgLSBuYW1lOiBhY2xcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGhpZGVfZ3JvdXBzX2hlYWRlcjogdHJ1ZVxuICAgICAgICAgIGFsbG93OlxuICAgICAgICAgICAgLSBhZG1pblxuICAgICAgICAgICAgLSBhbm9uXG5cbiAgIyMgU2VjdXJlIFJFU1Qgcm91dGVzXG4gIC0gbmFtZTogcmVzdC12MVxuICAgIF9jb21tZW50OiAnUG9zdGdSRVNUOiAvcmVzdC92MS8qIC0+IGh0dHA6Ly9zdXBhYmFzZS1yZXN0OjMwMDAvKidcbiAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1yZXN0OjMwMDAvXG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiByZXN0LXYxLWFsbFxuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL3Jlc3QvdjEvXG4gICAgcGx1Z2luczpcbiAgICAgIC0gbmFtZTogY29yc1xuICAgICAgLSBuYW1lOiBrZXktYXV0aFxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgaGlkZV9jcmVkZW50aWFsczogdHJ1ZVxuICAgICAgLSBuYW1lOiBhY2xcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGhpZGVfZ3JvdXBzX2hlYWRlcjogdHJ1ZVxuICAgICAgICAgIGFsbG93OlxuICAgICAgICAgICAgLSBhZG1pblxuICAgICAgICAgICAgLSBhbm9uXG5cbiAgIyMgU2VjdXJlIEdyYXBoUUwgcm91dGVzXG4gIC0gbmFtZTogZ3JhcGhxbC12MVxuICAgIF9jb21tZW50OiAnUG9zdGdSRVNUOiAvZ3JhcGhxbC92MS8qIC0+IGh0dHA6Ly9zdXBhYmFzZS1yZXN0OjMwMDAvcnBjL2dyYXBocWwnXG4gICAgdXJsOiBodHRwOi8vc3VwYWJhc2UtcmVzdDozMDAwL3JwYy9ncmFwaHFsXG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiBncmFwaHFsLXYxLWFsbFxuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL2dyYXBocWwvdjFcbiAgICBwbHVnaW5zOlxuICAgICAgLSBuYW1lOiBjb3JzXG4gICAgICAtIG5hbWU6IGtleS1hdXRoXG4gICAgICAgIGNvbmZpZzpcbiAgICAgICAgICBoaWRlX2NyZWRlbnRpYWxzOiB0cnVlXG4gICAgICAtIG5hbWU6IHJlcXVlc3QtdHJhbnNmb3JtZXJcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGFkZDpcbiAgICAgICAgICAgIGhlYWRlcnM6XG4gICAgICAgICAgICAgIC0gQ29udGVudC1Qcm9maWxlOmdyYXBocWxfcHVibGljXG4gICAgICAtIG5hbWU6IGFjbFxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgaGlkZV9ncm91cHNfaGVhZGVyOiB0cnVlXG4gICAgICAgICAgYWxsb3c6XG4gICAgICAgICAgICAtIGFkbWluXG4gICAgICAgICAgICAtIGFub25cblxuICAjIyBTZWN1cmUgUmVhbHRpbWUgcm91dGVzXG4gIC0gbmFtZTogcmVhbHRpbWUtdjEtd3NcbiAgICBfY29tbWVudDogJ1JlYWx0aW1lOiAvcmVhbHRpbWUvdjEvKiAtPiB3czovL3JlYWx0aW1lOjQwMDAvc29ja2V0LyonXG4gICAgdXJsOiBodHRwOi8vcmVhbHRpbWUtZGV2OjQwMDAvc29ja2V0XG4gICAgcHJvdG9jb2w6IHdzXG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiByZWFsdGltZS12MS13c1xuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL3JlYWx0aW1lL3YxL1xuICAgIHBsdWdpbnM6XG4gICAgICAtIG5hbWU6IGNvcnNcbiAgICAgIC0gbmFtZToga2V5LWF1dGhcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGhpZGVfY3JlZGVudGlhbHM6IGZhbHNlXG4gICAgICAtIG5hbWU6IGFjbFxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgaGlkZV9ncm91cHNfaGVhZGVyOiB0cnVlXG4gICAgICAgICAgYWxsb3c6XG4gICAgICAgICAgICAtIGFkbWluXG4gICAgICAgICAgICAtIGFub25cbiAgLSBuYW1lOiByZWFsdGltZS12MS1yZXN0XG4gICAgX2NvbW1lbnQ6ICdSZWFsdGltZTogL3JlYWx0aW1lL3YxLyogLT4gd3M6Ly9yZWFsdGltZTo0MDAwL3NvY2tldC8qJ1xuICAgIHVybDogaHR0cDovL3JlYWx0aW1lLWRldjo0MDAwL2FwaVxuICAgIHByb3RvY29sOiBodHRwXG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiByZWFsdGltZS12MS1yZXN0XG4gICAgICAgIHN0cmlwX3BhdGg6IHRydWVcbiAgICAgICAgcGF0aHM6XG4gICAgICAgICAgLSAvcmVhbHRpbWUvdjEvYXBpXG4gICAgcGx1Z2luczpcbiAgICAgIC0gbmFtZTogY29yc1xuICAgICAgLSBuYW1lOiBrZXktYXV0aFxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgaGlkZV9jcmVkZW50aWFsczogZmFsc2VcbiAgICAgIC0gbmFtZTogYWNsXG4gICAgICAgIGNvbmZpZzpcbiAgICAgICAgICBoaWRlX2dyb3Vwc19oZWFkZXI6IHRydWVcbiAgICAgICAgICBhbGxvdzpcbiAgICAgICAgICAgIC0gYWRtaW5cbiAgICAgICAgICAgIC0gYW5vblxuXG4gICMjIFN0b3JhZ2Ugcm91dGVzOiB0aGUgc3RvcmFnZSBzZXJ2ZXIgbWFuYWdlcyBpdHMgb3duIGF1dGhcbiAgLSBuYW1lOiBzdG9yYWdlLXYxXG4gICAgX2NvbW1lbnQ6ICdTdG9yYWdlOiAvc3RvcmFnZS92MS8qIC0+IGh0dHA6Ly9zdXBhYmFzZS1zdG9yYWdlOjUwMDAvKidcbiAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1zdG9yYWdlOjUwMDAvXG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiBzdG9yYWdlLXYxLWFsbFxuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL3N0b3JhZ2UvdjEvXG4gICAgcGx1Z2luczpcbiAgICAgIC0gbmFtZTogY29yc1xuXG4gICMjIEVkZ2UgRnVuY3Rpb25zIHJvdXRlc1xuICAtIG5hbWU6IGZ1bmN0aW9ucy12MVxuICAgIF9jb21tZW50OiAnRWRnZSBGdW5jdGlvbnM6IC9mdW5jdGlvbnMvdjEvKiAtPiBodHRwOi8vc3VwYWJhc2UtZWRnZS1mdW5jdGlvbnM6OTAwMC8qJ1xuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLWVkZ2UtZnVuY3Rpb25zOjkwMDAvXG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiBmdW5jdGlvbnMtdjEtYWxsXG4gICAgICAgIHN0cmlwX3BhdGg6IHRydWVcbiAgICAgICAgcGF0aHM6XG4gICAgICAgICAgLSAvZnVuY3Rpb25zL3YxL1xuICAgIHBsdWdpbnM6XG4gICAgICAtIG5hbWU6IGNvcnNcblxuICAjIyBBbmFseXRpY3Mgcm91dGVzXG4gIC0gbmFtZTogYW5hbHl0aWNzLXYxXG4gICAgX2NvbW1lbnQ6ICdBbmFseXRpY3M6IC9hbmFseXRpY3MvdjEvKiAtPiBodHRwOi8vbG9nZmxhcmU6NDAwMC8qJ1xuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLWFuYWx5dGljczo0MDAwL1xuICAgIHJvdXRlczpcbiAgICAgIC0gbmFtZTogYW5hbHl0aWNzLXYxLWFsbFxuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL2FuYWx5dGljcy92MS9cblxuICAjIyBTZWN1cmUgRGF0YWJhc2Ugcm91dGVzXG4gIC0gbmFtZTogbWV0YVxuICAgIF9jb21tZW50OiAncGctbWV0YTogL3BnLyogLT4gaHR0cDovL3N1cGFiYXNlLW1ldGE6ODA4MC8qJ1xuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLW1ldGE6ODA4MC9cbiAgICByb3V0ZXM6XG4gICAgICAtIG5hbWU6IG1ldGEtYWxsXG4gICAgICAgIHN0cmlwX3BhdGg6IHRydWVcbiAgICAgICAgcGF0aHM6XG4gICAgICAgICAgLSAvcGcvXG4gICAgcGx1Z2luczpcbiAgICAgIC0gbmFtZToga2V5LWF1dGhcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGhpZGVfY3JlZGVudGlhbHM6IGZhbHNlXG4gICAgICAtIG5hbWU6IGFjbFxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgaGlkZV9ncm91cHNfaGVhZGVyOiB0cnVlXG4gICAgICAgICAgYWxsb3c6XG4gICAgICAgICAgICAtIGFkbWluXG5cbiAgIyMgUHJvdGVjdGVkIERhc2hib2FyZCAtIGNhdGNoIGFsbCByZW1haW5pbmcgcm91dGVzXG4gIC0gbmFtZTogZGFzaGJvYXJkXG4gICAgX2NvbW1lbnQ6ICdTdHVkaW86IC8qIC0+IGh0dHA6Ly9zdHVkaW86MzAwMC8qJ1xuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLXN0dWRpbzozMDAwL1xuICAgIHJvdXRlczpcbiAgICAgIC0gbmFtZTogZGFzaGJvYXJkLWFsbFxuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL1xuICAgIHBsdWdpbnM6XG4gICAgICAtIG5hbWU6IGNvcnNcbiAgICAgIC0gbmFtZTogYmFzaWMtYXV0aFxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgaGlkZV9jcmVkZW50aWFsczogdHJ1ZVxuIgogIHN1cGFiYXNlLXN0dWRpbzoKICAgIGltYWdlOiAnc3VwYWJhc2Uvc3R1ZGlvOjIwMjQwNTE0LTZmNWNhYmQnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gbm9kZQogICAgICAgIC0gJy1lJwogICAgICAgIC0gInJlcXVpcmUoJ2h0dHAnKS5nZXQoJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMC9hcGkvcHJvZmlsZScsIChyKSA9PiB7aWYgKHIuc3RhdHVzQ29kZSAhPT0gMjAwKSBwcm9jZXNzLmV4aXQoMSk7IGVsc2UgcHJvY2Vzcy5leGl0KDApOyB9KS5vbignZXJyb3InLCAoKSA9PiBwcm9jZXNzLmV4aXQoMSkpIgogICAgICB0aW1lb3V0OiA1cwogICAgICBpbnRlcnZhbDogNXMKICAgICAgcmV0cmllczogMwogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtYW5hbHl0aWNzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBIT1NUTkFNRT0wLjAuMC4wCiAgICAgIC0gJ1NUVURJT19QR19NRVRBX1VSTD1odHRwOi8vc3VwYWJhc2UtbWV0YTo4MDgwJwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdERUZBVUxUX09SR0FOSVpBVElPTl9OQU1FPSR7U1RVRElPX0RFRkFVTFRfT1JHQU5JWkFUSU9OOi1EZWZhdWx0IE9yZ2FuaXphdGlvbn0nCiAgICAgIC0gJ0RFRkFVTFRfUFJPSkVDVF9OQU1FPSR7U1RVRElPX0RFRkFVTFRfUFJPSkVDVDotRGVmYXVsdCBQcm9qZWN0fScKICAgICAgLSAnU1VQQUJBU0VfVVJMPWh0dHA6Ly9zdXBhYmFzZS1rb25nOjgwMDAnCiAgICAgIC0gJ1NVUEFCQVNFX1BVQkxJQ19VUkw9JHtTRVJWSUNFX0ZRRE5fU1VQQUJBU0VLT05HfScKICAgICAgLSAnU1VQQUJBU0VfQU5PTl9LRVk9JHtTRVJWSUNFX1NVUEFCQVNFQU5PTl9LRVl9JwogICAgICAtICdTVVBBQkFTRV9TRVJWSUNFX0tFWT0ke1NFUlZJQ0VfU1VQQUJBU0VTRVJWSUNFX0tFWX0nCiAgICAgIC0gJ0FVVEhfSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfScKICAgICAgLSAnTE9HRkxBUkVfQVBJX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTE9HRkxBUkV9JwogICAgICAtICdMT0dGTEFSRV9VUkw9aHR0cDovL3N1cGFiYXNlLWFuYWx5dGljczo0MDAwJwogICAgICAtIE5FWFRfUFVCTElDX0VOQUJMRV9MT0dTPXRydWUKICAgICAgLSBORVhUX0FOQUxZVElDU19CQUNLRU5EX1BST1ZJREVSPXBvc3RncmVzCiAgc3VwYWJhc2UtZGI6CiAgICBpbWFnZTogJ3N1cGFiYXNlL3Bvc3RncmVzOjE1LjEuMS40MScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAncGdfaXNyZWFkeSAtVSBwb3N0Z3JlcyAtaCAxMjcuMC4wLjEnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiAxMAogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtdmVjdG9yOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBjb21tYW5kOgogICAgICAtIHBvc3RncmVzCiAgICAgIC0gJy1jJwogICAgICAtIGNvbmZpZ19maWxlPS9ldGMvcG9zdGdyZXNxbC9wb3N0Z3Jlc3FsLmNvbmYKICAgICAgLSAnLWMnCiAgICAgIC0gbG9nX21pbl9tZXNzYWdlcz1mYXRhbAogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX0hPU1Q9L3Zhci9ydW4vcG9zdGdyZXNxbAogICAgICAtICdQR1BPUlQ9JHtQT1NUR1JFU19QT1JUOi01NDMyfScKICAgICAgLSAnUE9TVEdSRVNfUE9SVD0ke1BPU1RHUkVTX1BPUlQ6LTU0MzJ9JwogICAgICAtICdQR1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gJ1BHREFUQUJBU0U9JHtQT1NUR1JFU19EQjotcG9zdGdyZXN9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1wb3N0Z3Jlc30nCiAgICAgIC0gJ0pXVF9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX0pXVH0nCiAgICAgIC0gJ0pXVF9FWFA9JHtKV1RfRVhQSVJZOi0zNjAwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3N1cGFiYXNlLWRiLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi92b2x1bWVzL2RiL3JlYWx0aW1lLnNxbAogICAgICAgIHRhcmdldDogL2RvY2tlci1lbnRyeXBvaW50LWluaXRkYi5kL21pZ3JhdGlvbnMvOTktcmVhbHRpbWUuc3FsCiAgICAgICAgY29udGVudDogIlxcc2V0IHBndXNlciBgZWNobyBcInN1cGFiYXNlX2FkbWluXCJgXG5cbmNyZWF0ZSBzY2hlbWEgaWYgbm90IGV4aXN0cyBfcmVhbHRpbWU7XG5hbHRlciBzY2hlbWEgX3JlYWx0aW1lIG93bmVyIHRvIDpwZ3VzZXI7XG4iCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL3ZvbHVtZXMvZGIvd2ViaG9va3Muc3FsCiAgICAgICAgdGFyZ2V0OiAvZG9ja2VyLWVudHJ5cG9pbnQtaW5pdGRiLmQvaW5pdC1zY3JpcHRzLzk4LXdlYmhvb2tzLnNxbAogICAgICAgIGNvbnRlbnQ6ICJCRUdJTjtcbi0tIENyZWF0ZSBwZ19uZXQgZXh0ZW5zaW9uXG5DUkVBVEUgRVhURU5TSU9OIElGIE5PVCBFWElTVFMgcGdfbmV0IFNDSEVNQSBleHRlbnNpb25zO1xuLS0gQ3JlYXRlIHN1cGFiYXNlX2Z1bmN0aW9ucyBzY2hlbWFcbkNSRUFURSBTQ0hFTUEgc3VwYWJhc2VfZnVuY3Rpb25zIEFVVEhPUklaQVRJT04gc3VwYWJhc2VfYWRtaW47XG5HUkFOVCBVU0FHRSBPTiBTQ0hFTUEgc3VwYWJhc2VfZnVuY3Rpb25zIFRPIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG5BTFRFUiBERUZBVUxUIFBSSVZJTEVHRVMgSU4gU0NIRU1BIHN1cGFiYXNlX2Z1bmN0aW9ucyBHUkFOVCBBTEwgT04gVEFCTEVTIFRPIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG5BTFRFUiBERUZBVUxUIFBSSVZJTEVHRVMgSU4gU0NIRU1BIHN1cGFiYXNlX2Z1bmN0aW9ucyBHUkFOVCBBTEwgT04gRlVOQ1RJT05TIFRPIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG5BTFRFUiBERUZBVUxUIFBSSVZJTEVHRVMgSU4gU0NIRU1BIHN1cGFiYXNlX2Z1bmN0aW9ucyBHUkFOVCBBTEwgT04gU0VRVUVOQ0VTIFRPIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG4tLSBzdXBhYmFzZV9mdW5jdGlvbnMubWlncmF0aW9ucyBkZWZpbml0aW9uXG5DUkVBVEUgVEFCTEUgc3VwYWJhc2VfZnVuY3Rpb25zLm1pZ3JhdGlvbnMgKFxuICB2ZXJzaW9uIHRleHQgUFJJTUFSWSBLRVksXG4gIGluc2VydGVkX2F0IHRpbWVzdGFtcHR6IE5PVCBOVUxMIERFRkFVTFQgTk9XKClcbik7XG4tLSBJbml0aWFsIHN1cGFiYXNlX2Z1bmN0aW9ucyBtaWdyYXRpb25cbklOU0VSVCBJTlRPIHN1cGFiYXNlX2Z1bmN0aW9ucy5taWdyYXRpb25zICh2ZXJzaW9uKSBWQUxVRVMgKCdpbml0aWFsJyk7XG4tLSBzdXBhYmFzZV9mdW5jdGlvbnMuaG9va3MgZGVmaW5pdGlvblxuQ1JFQVRFIFRBQkxFIHN1cGFiYXNlX2Z1bmN0aW9ucy5ob29rcyAoXG4gIGlkIGJpZ3NlcmlhbCBQUklNQVJZIEtFWSxcbiAgaG9va190YWJsZV9pZCBpbnRlZ2VyIE5PVCBOVUxMLFxuICBob29rX25hbWUgdGV4dCBOT1QgTlVMTCxcbiAgY3JlYXRlZF9hdCB0aW1lc3RhbXB0eiBOT1QgTlVMTCBERUZBVUxUIE5PVygpLFxuICByZXF1ZXN0X2lkIGJpZ2ludFxuKTtcbkNSRUFURSBJTkRFWCBzdXBhYmFzZV9mdW5jdGlvbnNfaG9va3NfcmVxdWVzdF9pZF9pZHggT04gc3VwYWJhc2VfZnVuY3Rpb25zLmhvb2tzIFVTSU5HIGJ0cmVlIChyZXF1ZXN0X2lkKTtcbkNSRUFURSBJTkRFWCBzdXBhYmFzZV9mdW5jdGlvbnNfaG9va3NfaF90YWJsZV9pZF9oX25hbWVfaWR4IE9OIHN1cGFiYXNlX2Z1bmN0aW9ucy5ob29rcyBVU0lORyBidHJlZSAoaG9va190YWJsZV9pZCwgaG9va19uYW1lKTtcbkNPTU1FTlQgT04gVEFCTEUgc3VwYWJhc2VfZnVuY3Rpb25zLmhvb2tzIElTICdTdXBhYmFzZSBGdW5jdGlvbnMgSG9va3M6IEF1ZGl0IHRyYWlsIGZvciB0cmlnZ2VyZWQgaG9va3MuJztcbkNSRUFURSBGVU5DVElPTiBzdXBhYmFzZV9mdW5jdGlvbnMuaHR0cF9yZXF1ZXN0KClcbiAgUkVUVVJOUyB0cmlnZ2VyXG4gIExBTkdVQUdFIHBscGdzcWxcbiAgQVMgJGZ1bmN0aW9uJFxuICBERUNMQVJFXG4gICAgcmVxdWVzdF9pZCBiaWdpbnQ7XG4gICAgcGF5bG9hZCBqc29uYjtcbiAgICB1cmwgdGV4dCA6PSBUR19BUkdWWzBdOjp0ZXh0O1xuICAgIG1ldGhvZCB0ZXh0IDo9IFRHX0FSR1ZbMV06OnRleHQ7XG4gICAgaGVhZGVycyBqc29uYiBERUZBVUxUICd7fSc6Ompzb25iO1xuICAgIHBhcmFtcyBqc29uYiBERUZBVUxUICd7fSc6Ompzb25iO1xuICAgIHRpbWVvdXRfbXMgaW50ZWdlciBERUZBVUxUIDEwMDA7XG4gIEJFR0lOXG4gICAgSUYgdXJsIElTIE5VTEwgT1IgdXJsID0gJ251bGwnIFRIRU5cbiAgICAgIFJBSVNFIEVYQ0VQVElPTiAndXJsIGFyZ3VtZW50IGlzIG1pc3NpbmcnO1xuICAgIEVORCBJRjtcblxuICAgIElGIG1ldGhvZCBJUyBOVUxMIE9SIG1ldGhvZCA9ICdudWxsJyBUSEVOXG4gICAgICBSQUlTRSBFWENFUFRJT04gJ21ldGhvZCBhcmd1bWVudCBpcyBtaXNzaW5nJztcbiAgICBFTkQgSUY7XG5cbiAgICBJRiBUR19BUkdWWzJdIElTIE5VTEwgT1IgVEdfQVJHVlsyXSA9ICdudWxsJyBUSEVOXG4gICAgICBoZWFkZXJzID0gJ3tcIkNvbnRlbnQtVHlwZVwiOiBcImFwcGxpY2F0aW9uL2pzb25cIn0nOjpqc29uYjtcbiAgICBFTFNFXG4gICAgICBoZWFkZXJzID0gVEdfQVJHVlsyXTo6anNvbmI7XG4gICAgRU5EIElGO1xuXG4gICAgSUYgVEdfQVJHVlszXSBJUyBOVUxMIE9SIFRHX0FSR1ZbM10gPSAnbnVsbCcgVEhFTlxuICAgICAgcGFyYW1zID0gJ3t9Jzo6anNvbmI7XG4gICAgRUxTRVxuICAgICAgcGFyYW1zID0gVEdfQVJHVlszXTo6anNvbmI7XG4gICAgRU5EIElGO1xuXG4gICAgSUYgVEdfQVJHVls0XSBJUyBOVUxMIE9SIFRHX0FSR1ZbNF0gPSAnbnVsbCcgVEhFTlxuICAgICAgdGltZW91dF9tcyA9IDEwMDA7XG4gICAgRUxTRVxuICAgICAgdGltZW91dF9tcyA9IFRHX0FSR1ZbNF06OmludGVnZXI7XG4gICAgRU5EIElGO1xuXG4gICAgQ0FTRVxuICAgICAgV0hFTiBtZXRob2QgPSAnR0VUJyBUSEVOXG4gICAgICAgIFNFTEVDVCBodHRwX2dldCBJTlRPIHJlcXVlc3RfaWQgRlJPTSBuZXQuaHR0cF9nZXQoXG4gICAgICAgICAgdXJsLFxuICAgICAgICAgIHBhcmFtcyxcbiAgICAgICAgICBoZWFkZXJzLFxuICAgICAgICAgIHRpbWVvdXRfbXNcbiAgICAgICAgKTtcbiAgICAgIFdIRU4gbWV0aG9kID0gJ1BPU1QnIFRIRU5cbiAgICAgICAgcGF5bG9hZCA9IGpzb25iX2J1aWxkX29iamVjdChcbiAgICAgICAgICAnb2xkX3JlY29yZCcsIE9MRCxcbiAgICAgICAgICAncmVjb3JkJywgTkVXLFxuICAgICAgICAgICd0eXBlJywgVEdfT1AsXG4gICAgICAgICAgJ3RhYmxlJywgVEdfVEFCTEVfTkFNRSxcbiAgICAgICAgICAnc2NoZW1hJywgVEdfVEFCTEVfU0NIRU1BXG4gICAgICAgICk7XG5cbiAgICAgICAgU0VMRUNUIGh0dHBfcG9zdCBJTlRPIHJlcXVlc3RfaWQgRlJPTSBuZXQuaHR0cF9wb3N0KFxuICAgICAgICAgIHVybCxcbiAgICAgICAgICBwYXlsb2FkLFxuICAgICAgICAgIHBhcmFtcyxcbiAgICAgICAgICBoZWFkZXJzLFxuICAgICAgICAgIHRpbWVvdXRfbXNcbiAgICAgICAgKTtcbiAgICAgIEVMU0VcbiAgICAgICAgUkFJU0UgRVhDRVBUSU9OICdtZXRob2QgYXJndW1lbnQgJSBpcyBpbnZhbGlkJywgbWV0aG9kO1xuICAgIEVORCBDQVNFO1xuXG4gICAgSU5TRVJUIElOVE8gc3VwYWJhc2VfZnVuY3Rpb25zLmhvb2tzXG4gICAgICAoaG9va190YWJsZV9pZCwgaG9va19uYW1lLCByZXF1ZXN0X2lkKVxuICAgIFZBTFVFU1xuICAgICAgKFRHX1JFTElELCBUR19OQU1FLCByZXF1ZXN0X2lkKTtcblxuICAgIFJFVFVSTiBORVc7XG4gIEVORFxuJGZ1bmN0aW9uJDtcbi0tIFN1cGFiYXNlIHN1cGVyIGFkbWluXG5ET1xuJCRcbkJFR0lOXG4gIElGIE5PVCBFWElTVFMgKFxuICAgIFNFTEVDVCAxXG4gICAgRlJPTSBwZ19yb2xlc1xuICAgIFdIRVJFIHJvbG5hbWUgPSAnc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluJ1xuICApXG4gIFRIRU5cbiAgICBDUkVBVEUgVVNFUiBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW4gTk9JTkhFUklUIENSRUFURVJPTEUgTE9HSU4gTk9SRVBMSUNBVElPTjtcbiAgRU5EIElGO1xuRU5EXG4kJDtcbkdSQU5UIEFMTCBQUklWSUxFR0VTIE9OIFNDSEVNQSBzdXBhYmFzZV9mdW5jdGlvbnMgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluO1xuR1JBTlQgQUxMIFBSSVZJTEVHRVMgT04gQUxMIFRBQkxFUyBJTiBTQ0hFTUEgc3VwYWJhc2VfZnVuY3Rpb25zIFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbjtcbkdSQU5UIEFMTCBQUklWSUxFR0VTIE9OIEFMTCBTRVFVRU5DRVMgSU4gU0NIRU1BIHN1cGFiYXNlX2Z1bmN0aW9ucyBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW47XG5BTFRFUiBVU0VSIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiBTRVQgc2VhcmNoX3BhdGggPSBcInN1cGFiYXNlX2Z1bmN0aW9uc1wiO1xuQUxURVIgdGFibGUgXCJzdXBhYmFzZV9mdW5jdGlvbnNcIi5taWdyYXRpb25zIE9XTkVSIFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbjtcbkFMVEVSIHRhYmxlIFwic3VwYWJhc2VfZnVuY3Rpb25zXCIuaG9va3MgT1dORVIgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluO1xuQUxURVIgZnVuY3Rpb24gXCJzdXBhYmFzZV9mdW5jdGlvbnNcIi5odHRwX3JlcXVlc3QoKSBPV05FUiBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW47XG5HUkFOVCBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW4gVE8gcG9zdGdyZXM7XG4tLSBSZW1vdmUgdW51c2VkIHN1cGFiYXNlX3BnX25ldF9hZG1pbiByb2xlXG5ET1xuJCRcbkJFR0lOXG4gIElGIEVYSVNUUyAoXG4gICAgU0VMRUNUIDFcbiAgICBGUk9NIHBnX3JvbGVzXG4gICAgV0hFUkUgcm9sbmFtZSA9ICdzdXBhYmFzZV9wZ19uZXRfYWRtaW4nXG4gIClcbiAgVEhFTlxuICAgIFJFQVNTSUdOIE9XTkVEIEJZIHN1cGFiYXNlX3BnX25ldF9hZG1pbiBUTyBzdXBhYmFzZV9hZG1pbjtcbiAgICBEUk9QIE9XTkVEIEJZIHN1cGFiYXNlX3BnX25ldF9hZG1pbjtcbiAgICBEUk9QIFJPTEUgc3VwYWJhc2VfcGdfbmV0X2FkbWluO1xuICBFTkQgSUY7XG5FTkRcbiQkO1xuLS0gcGdfbmV0IGdyYW50cyB3aGVuIGV4dGVuc2lvbiBpcyBhbHJlYWR5IGVuYWJsZWRcbkRPXG4kJFxuQkVHSU5cbiAgSUYgRVhJU1RTIChcbiAgICBTRUxFQ1QgMVxuICAgIEZST00gcGdfZXh0ZW5zaW9uXG4gICAgV0hFUkUgZXh0bmFtZSA9ICdwZ19uZXQnXG4gIClcbiAgVEhFTlxuICAgIEdSQU5UIFVTQUdFIE9OIFNDSEVNQSBuZXQgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluLCBwb3N0Z3JlcywgYW5vbiwgYXV0aGVudGljYXRlZCwgc2VydmljZV9yb2xlO1xuICAgIEFMVEVSIGZ1bmN0aW9uIG5ldC5odHRwX2dldCh1cmwgdGV4dCwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBTRUNVUklUWSBERUZJTkVSO1xuICAgIEFMVEVSIGZ1bmN0aW9uIG5ldC5odHRwX3Bvc3QodXJsIHRleHQsIGJvZHkganNvbmIsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgU0VDVVJJVFkgREVGSU5FUjtcbiAgICBBTFRFUiBmdW5jdGlvbiBuZXQuaHR0cF9nZXQodXJsIHRleHQsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgU0VUIHNlYXJjaF9wYXRoID0gbmV0O1xuICAgIEFMVEVSIGZ1bmN0aW9uIG5ldC5odHRwX3Bvc3QodXJsIHRleHQsIGJvZHkganNvbmIsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgU0VUIHNlYXJjaF9wYXRoID0gbmV0O1xuICAgIFJFVk9LRSBBTEwgT04gRlVOQ1RJT04gbmV0Lmh0dHBfZ2V0KHVybCB0ZXh0LCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIEZST00gUFVCTElDO1xuICAgIFJFVk9LRSBBTEwgT04gRlVOQ1RJT04gbmV0Lmh0dHBfcG9zdCh1cmwgdGV4dCwgYm9keSBqc29uYiwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBGUk9NIFBVQkxJQztcbiAgICBHUkFOVCBFWEVDVVRFIE9OIEZVTkNUSU9OIG5ldC5odHRwX2dldCh1cmwgdGV4dCwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW4sIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG4gICAgR1JBTlQgRVhFQ1VURSBPTiBGVU5DVElPTiBuZXQuaHR0cF9wb3N0KHVybCB0ZXh0LCBib2R5IGpzb25iLCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiwgcG9zdGdyZXMsIGFub24sIGF1dGhlbnRpY2F0ZWQsIHNlcnZpY2Vfcm9sZTtcbiAgRU5EIElGO1xuRU5EXG4kJDtcbi0tIEV2ZW50IHRyaWdnZXIgZm9yIHBnX25ldFxuQ1JFQVRFIE9SIFJFUExBQ0UgRlVOQ1RJT04gZXh0ZW5zaW9ucy5ncmFudF9wZ19uZXRfYWNjZXNzKClcblJFVFVSTlMgZXZlbnRfdHJpZ2dlclxuTEFOR1VBR0UgcGxwZ3NxbFxuQVMgJCRcbkJFR0lOXG4gIElGIEVYSVNUUyAoXG4gICAgU0VMRUNUIDFcbiAgICBGUk9NIHBnX2V2ZW50X3RyaWdnZXJfZGRsX2NvbW1hbmRzKCkgQVMgZXZcbiAgICBKT0lOIHBnX2V4dGVuc2lvbiBBUyBleHRcbiAgICBPTiBldi5vYmppZCA9IGV4dC5vaWRcbiAgICBXSEVSRSBleHQuZXh0bmFtZSA9ICdwZ19uZXQnXG4gIClcbiAgVEhFTlxuICAgIEdSQU5UIFVTQUdFIE9OIFNDSEVNQSBuZXQgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluLCBwb3N0Z3JlcywgYW5vbiwgYXV0aGVudGljYXRlZCwgc2VydmljZV9yb2xlO1xuICAgIEFMVEVSIGZ1bmN0aW9uIG5ldC5odHRwX2dldCh1cmwgdGV4dCwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBTRUNVUklUWSBERUZJTkVSO1xuICAgIEFMVEVSIGZ1bmN0aW9uIG5ldC5odHRwX3Bvc3QodXJsIHRleHQsIGJvZHkganNvbmIsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgU0VDVVJJVFkgREVGSU5FUjtcbiAgICBBTFRFUiBmdW5jdGlvbiBuZXQuaHR0cF9nZXQodXJsIHRleHQsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgU0VUIHNlYXJjaF9wYXRoID0gbmV0O1xuICAgIEFMVEVSIGZ1bmN0aW9uIG5ldC5odHRwX3Bvc3QodXJsIHRleHQsIGJvZHkganNvbmIsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgU0VUIHNlYXJjaF9wYXRoID0gbmV0O1xuICAgIFJFVk9LRSBBTEwgT04gRlVOQ1RJT04gbmV0Lmh0dHBfZ2V0KHVybCB0ZXh0LCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIEZST00gUFVCTElDO1xuICAgIFJFVk9LRSBBTEwgT04gRlVOQ1RJT04gbmV0Lmh0dHBfcG9zdCh1cmwgdGV4dCwgYm9keSBqc29uYiwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBGUk9NIFBVQkxJQztcbiAgICBHUkFOVCBFWEVDVVRFIE9OIEZVTkNUSU9OIG5ldC5odHRwX2dldCh1cmwgdGV4dCwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW4sIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG4gICAgR1JBTlQgRVhFQ1VURSBPTiBGVU5DVElPTiBuZXQuaHR0cF9wb3N0KHVybCB0ZXh0LCBib2R5IGpzb25iLCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiwgcG9zdGdyZXMsIGFub24sIGF1dGhlbnRpY2F0ZWQsIHNlcnZpY2Vfcm9sZTtcbiAgRU5EIElGO1xuRU5EO1xuJCQ7XG5DT01NRU5UIE9OIEZVTkNUSU9OIGV4dGVuc2lvbnMuZ3JhbnRfcGdfbmV0X2FjY2VzcyBJUyAnR3JhbnRzIGFjY2VzcyB0byBwZ19uZXQnO1xuRE9cbiQkXG5CRUdJTlxuICBJRiBOT1QgRVhJU1RTIChcbiAgICBTRUxFQ1QgMVxuICAgIEZST00gcGdfZXZlbnRfdHJpZ2dlclxuICAgIFdIRVJFIGV2dG5hbWUgPSAnaXNzdWVfcGdfbmV0X2FjY2VzcydcbiAgKSBUSEVOXG4gICAgQ1JFQVRFIEVWRU5UIFRSSUdHRVIgaXNzdWVfcGdfbmV0X2FjY2VzcyBPTiBkZGxfY29tbWFuZF9lbmQgV0hFTiBUQUcgSU4gKCdDUkVBVEUgRVhURU5TSU9OJylcbiAgICBFWEVDVVRFIFBST0NFRFVSRSBleHRlbnNpb25zLmdyYW50X3BnX25ldF9hY2Nlc3MoKTtcbiAgRU5EIElGO1xuRU5EXG4kJDtcbklOU0VSVCBJTlRPIHN1cGFiYXNlX2Z1bmN0aW9ucy5taWdyYXRpb25zICh2ZXJzaW9uKSBWQUxVRVMgKCcyMDIxMDgwOTE4MzQyM191cGRhdGVfZ3JhbnRzJyk7XG5BTFRFUiBmdW5jdGlvbiBzdXBhYmFzZV9mdW5jdGlvbnMuaHR0cF9yZXF1ZXN0KCkgU0VDVVJJVFkgREVGSU5FUjtcbkFMVEVSIGZ1bmN0aW9uIHN1cGFiYXNlX2Z1bmN0aW9ucy5odHRwX3JlcXVlc3QoKSBTRVQgc2VhcmNoX3BhdGggPSBzdXBhYmFzZV9mdW5jdGlvbnM7XG5SRVZPS0UgQUxMIE9OIEZVTkNUSU9OIHN1cGFiYXNlX2Z1bmN0aW9ucy5odHRwX3JlcXVlc3QoKSBGUk9NIFBVQkxJQztcbkdSQU5UIEVYRUNVVEUgT04gRlVOQ1RJT04gc3VwYWJhc2VfZnVuY3Rpb25zLmh0dHBfcmVxdWVzdCgpIFRPIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG5DT01NSVQ7XG4iCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL3ZvbHVtZXMvZGIvcm9sZXMuc3FsCiAgICAgICAgdGFyZ2V0OiAvZG9ja2VyLWVudHJ5cG9pbnQtaW5pdGRiLmQvaW5pdC1zY3JpcHRzLzk5LXJvbGVzLnNxbAogICAgICAgIGNvbnRlbnQ6ICItLSBOT1RFOiBjaGFuZ2UgdG8geW91ciBvd24gcGFzc3dvcmRzIGZvciBwcm9kdWN0aW9uIGVudmlyb25tZW50c1xuIFxcc2V0IHBncGFzcyBgZWNobyBcIiRQT1NUR1JFU19QQVNTV09SRFwiYFxuXG4gQUxURVIgVVNFUiBhdXRoZW50aWNhdG9yIFdJVEggUEFTU1dPUkQgOidwZ3Bhc3MnO1xuIEFMVEVSIFVTRVIgcGdib3VuY2VyIFdJVEggUEFTU1dPUkQgOidwZ3Bhc3MnO1xuIEFMVEVSIFVTRVIgc3VwYWJhc2VfYXV0aF9hZG1pbiBXSVRIIFBBU1NXT1JEIDoncGdwYXNzJztcbiBBTFRFUiBVU0VSIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiBXSVRIIFBBU1NXT1JEIDoncGdwYXNzJztcbiBBTFRFUiBVU0VSIHN1cGFiYXNlX3N0b3JhZ2VfYWRtaW4gV0lUSCBQQVNTV09SRCA6J3BncGFzcyc7XG4iCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL3ZvbHVtZXMvZGIvand0LnNxbAogICAgICAgIHRhcmdldDogL2RvY2tlci1lbnRyeXBvaW50LWluaXRkYi5kL2luaXQtc2NyaXB0cy85OS1qd3Quc3FsCiAgICAgICAgY29udGVudDogIlxcc2V0IGp3dF9zZWNyZXQgYGVjaG8gXCIkSldUX1NFQ1JFVFwiYFxuXFxzZXQgand0X2V4cCBgZWNobyBcIiRKV1RfRVhQXCJgXG5cXHNldCBkYl9uYW1lIGBlY2hvIFwiJHtQT1NUR1JFU19EQjotcG9zdGdyZXN9XCJgXG5cbkFMVEVSIERBVEFCQVNFIDpkYl9uYW1lIFNFVCBcImFwcC5zZXR0aW5ncy5qd3Rfc2VjcmV0XCIgVE8gOidqd3Rfc2VjcmV0JztcbkFMVEVSIERBVEFCQVNFIDpkYl9uYW1lIFNFVCBcImFwcC5zZXR0aW5ncy5qd3RfZXhwXCIgVE8gOidqd3RfZXhwJztcbiIKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vdm9sdW1lcy9kYi9sb2dzLnNxbAogICAgICAgIHRhcmdldDogL2RvY2tlci1lbnRyeXBvaW50LWluaXRkYi5kL21pZ3JhdGlvbnMvOTktbG9ncy5zcWwKICAgICAgICBjb250ZW50OiAiXFxzZXQgcGd1c2VyIGBlY2hvIFwic3VwYWJhc2VfYWRtaW5cImBcblxuY3JlYXRlIHNjaGVtYSBpZiBub3QgZXhpc3RzIF9hbmFseXRpY3M7XG5hbHRlciBzY2hlbWEgX2FuYWx5dGljcyBvd25lciB0byA6cGd1c2VyO1xuIgogICAgICAtICdzdXBhYmFzZS1kYi1jb25maWc6L2V0Yy9wb3N0Z3Jlc3FsLWN1c3RvbScKICBzdXBhYmFzZS1hbmFseXRpY3M6CiAgICBpbWFnZTogJ3N1cGFiYXNlL2xvZ2ZsYXJlOjEuNC4wJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjQwMDAvaGVhbHRoJwogICAgICB0aW1lb3V0OiA1cwogICAgICBpbnRlcnZhbDogNXMKICAgICAgcmV0cmllczogMTAKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBkZXBlbmRzX29uOgogICAgICBzdXBhYmFzZS1kYjoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTE9HRkxBUkVfTk9ERV9IT1NUPTEyNy4wLjAuMQogICAgICAtIERCX1VTRVJOQU1FPXN1cGFiYXNlX2FkbWluCiAgICAgIC0gJ0RCX0RBVEFCQVNFPSR7UE9TVEdSRVNfREI6LXBvc3RncmVzfScKICAgICAgLSAnREJfSE9TVE5BTUU9JHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn0nCiAgICAgIC0gJ0RCX1BPUlQ9JHtQT1NUR1JFU19QT1JUOi01NDMyfScKICAgICAgLSAnREJfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfScKICAgICAgLSBEQl9TQ0hFTUE9X2FuYWx5dGljcwogICAgICAtICdMT0dGTEFSRV9BUElfS0VZPSR7U0VSVklDRV9QQVNTV09SRF9MT0dGTEFSRX0nCiAgICAgIC0gTE9HRkxBUkVfU0lOR0xFX1RFTkFOVD10cnVlCiAgICAgIC0gTE9HRkxBUkVfU0lOR0xFX1RFTkFOVF9NT0RFPXRydWUKICAgICAgLSBMT0dGTEFSRV9TVVBBQkFTRV9NT0RFPXRydWUKICAgICAgLSBMT0dGTEFSRV9NSU5fQ0xVU1RFUl9TSVpFPTEKICAgICAgLSAnUE9TVEdSRVNfQkFDS0VORF9VUkw9cG9zdGdyZXNxbDovL3N1cGFiYXNlX2FkbWluOiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AJHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn06JHtQT1NUR1JFU19QT1JUOi01NDMyfS8ke1BPU1RHUkVTX0RCOi1wb3N0Z3Jlc30nCiAgICAgIC0gUE9TVEdSRVNfQkFDS0VORF9TQ0hFTUE9X2FuYWx5dGljcwogICAgICAtIExPR0ZMQVJFX0ZFQVRVUkVfRkxBR19PVkVSUklERT1tdWx0aWJhY2tlbmQ9dHJ1ZQogIHN1cGFiYXNlLXZlY3RvcjoKICAgIGltYWdlOiAndGltYmVyaW8vdmVjdG9yOjAuMjguMS1hbHBpbmUnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gd2dldAogICAgICAgIC0gJy0tbm8tdmVyYm9zZScKICAgICAgICAtICctLXRyaWVzPTEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovL3N1cGFiYXNlLXZlY3Rvcjo5MDAxL2hlYWx0aCcKICAgICAgdGltZW91dDogNXMKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHJldHJpZXM6IDMKICAgIHZvbHVtZXM6CiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL3ZvbHVtZXMvbG9ncy92ZWN0b3IueW1sCiAgICAgICAgdGFyZ2V0OiAvZXRjL3ZlY3Rvci92ZWN0b3IueW1sCiAgICAgICAgcmVhZF9vbmx5OiB0cnVlCiAgICAgICAgY29udGVudDogImFwaTpcbiAgZW5hYmxlZDogdHJ1ZVxuICBhZGRyZXNzOiAwLjAuMC4wOjkwMDFcblxuc291cmNlczpcbiAgZG9ja2VyX2hvc3Q6XG4gICAgdHlwZTogZG9ja2VyX2xvZ3NcbiAgICBleGNsdWRlX2NvbnRhaW5lcnM6XG4gICAgICAtIHN1cGFiYXNlLXZlY3RvclxuXG50cmFuc2Zvcm1zOlxuICBwcm9qZWN0X2xvZ3M6XG4gICAgdHlwZTogcmVtYXBcbiAgICBpbnB1dHM6XG4gICAgICAtIGRvY2tlcl9ob3N0XG4gICAgc291cmNlOiB8LVxuICAgICAgLnByb2plY3QgPSBcImRlZmF1bHRcIlxuICAgICAgLmV2ZW50X21lc3NhZ2UgPSBkZWwoLm1lc3NhZ2UpXG4gICAgICAuYXBwbmFtZSA9IGRlbCguY29udGFpbmVyX25hbWUpXG4gICAgICBkZWwoLmNvbnRhaW5lcl9jcmVhdGVkX2F0KVxuICAgICAgZGVsKC5jb250YWluZXJfaWQpXG4gICAgICBkZWwoLnNvdXJjZV90eXBlKVxuICAgICAgZGVsKC5zdHJlYW0pXG4gICAgICBkZWwoLmxhYmVsKVxuICAgICAgZGVsKC5pbWFnZSlcbiAgICAgIGRlbCguaG9zdClcbiAgICAgIGRlbCguc3RyZWFtKVxuICByb3V0ZXI6XG4gICAgdHlwZTogcm91dGVcbiAgICBpbnB1dHM6XG4gICAgICAtIHByb2plY3RfbG9nc1xuICAgIHJvdXRlOlxuICAgICAga29uZzogJ3N0YXJ0c193aXRoKHN0cmluZyEoLmFwcG5hbWUpLCBcInN1cGFiYXNlLWtvbmdcIiknXG4gICAgICBhdXRoOiAnc3RhcnRzX3dpdGgoc3RyaW5nISguYXBwbmFtZSksIFwic3VwYWJhc2UtYXV0aFwiKSdcbiAgICAgIHJlc3Q6ICdzdGFydHNfd2l0aChzdHJpbmchKC5hcHBuYW1lKSwgXCJzdXBhYmFzZS1yZXN0XCIpJ1xuICAgICAgcmVhbHRpbWU6ICdzdGFydHNfd2l0aChzdHJpbmchKC5hcHBuYW1lKSwgXCJyZWFsdGltZS1kZXZcIiknXG4gICAgICBzdG9yYWdlOiAnc3RhcnRzX3dpdGgoc3RyaW5nISguYXBwbmFtZSksIFwic3VwYWJhc2Utc3RvcmFnZVwiKSdcbiAgICAgIGZ1bmN0aW9uczogJ3N0YXJ0c193aXRoKHN0cmluZyEoLmFwcG5hbWUpLCBcInN1cGFiYXNlLWZ1bmN0aW9uc1wiKSdcbiAgICAgIGRiOiAnc3RhcnRzX3dpdGgoc3RyaW5nISguYXBwbmFtZSksIFwic3VwYWJhc2UtZGJcIiknXG4gICMgSWdub3JlcyBub24gbmdpbnggZXJyb3JzIHNpbmNlIHRoZXkgYXJlIHJlbGF0ZWQgd2l0aCBrb25nIGJvb3RpbmcgdXBcbiAga29uZ19sb2dzOlxuICAgIHR5cGU6IHJlbWFwXG4gICAgaW5wdXRzOlxuICAgICAgLSByb3V0ZXIua29uZ1xuICAgIHNvdXJjZTogfC1cbiAgICAgIHJlcSwgZXJyID0gcGFyc2VfbmdpbnhfbG9nKC5ldmVudF9tZXNzYWdlLCBcImNvbWJpbmVkXCIpXG4gICAgICBpZiBlcnIgPT0gbnVsbCB7XG4gICAgICAgICAgLnRpbWVzdGFtcCA9IHJlcS50aW1lc3RhbXBcbiAgICAgICAgICAubWV0YWRhdGEucmVxdWVzdC5oZWFkZXJzLnJlZmVyZXIgPSByZXEucmVmZXJlclxuICAgICAgICAgIC5tZXRhZGF0YS5yZXF1ZXN0LmhlYWRlcnMudXNlcl9hZ2VudCA9IHJlcS5hZ2VudFxuICAgICAgICAgIC5tZXRhZGF0YS5yZXF1ZXN0LmhlYWRlcnMuY2ZfY29ubmVjdGluZ19pcCA9IHJlcS5jbGllbnRcbiAgICAgICAgICAubWV0YWRhdGEucmVxdWVzdC5tZXRob2QgPSByZXEubWV0aG9kXG4gICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QucGF0aCA9IHJlcS5wYXRoXG4gICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QucHJvdG9jb2wgPSByZXEucHJvdG9jb2xcbiAgICAgICAgICAubWV0YWRhdGEucmVzcG9uc2Uuc3RhdHVzX2NvZGUgPSByZXEuc3RhdHVzXG4gICAgICB9XG4gICAgICBpZiBlcnIgIT0gbnVsbCB7XG4gICAgICAgIGFib3J0XG4gICAgICB9XG4gICMgSWdub3JlcyBub24gbmdpbnggZXJyb3JzIHNpbmNlIHRoZXkgYXJlIHJlbGF0ZWQgd2l0aCBrb25nIGJvb3RpbmcgdXBcbiAga29uZ19lcnI6XG4gICAgdHlwZTogcmVtYXBcbiAgICBpbnB1dHM6XG4gICAgICAtIHJvdXRlci5rb25nXG4gICAgc291cmNlOiB8LVxuICAgICAgLm1ldGFkYXRhLnJlcXVlc3QubWV0aG9kID0gXCJHRVRcIlxuICAgICAgLm1ldGFkYXRhLnJlc3BvbnNlLnN0YXR1c19jb2RlID0gMjAwXG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX25naW54X2xvZyguZXZlbnRfbWVzc2FnZSwgXCJlcnJvclwiKVxuICAgICAgaWYgZXJyID09IG51bGwge1xuICAgICAgICAgIC50aW1lc3RhbXAgPSBwYXJzZWQudGltZXN0YW1wXG4gICAgICAgICAgLnNldmVyaXR5ID0gcGFyc2VkLnNldmVyaXR5XG4gICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QuaG9zdCA9IHBhcnNlZC5ob3N0XG4gICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QuaGVhZGVycy5jZl9jb25uZWN0aW5nX2lwID0gcGFyc2VkLmNsaWVudFxuICAgICAgICAgIHVybCwgZXJyID0gc3BsaXQocGFyc2VkLnJlcXVlc3QsIFwiIFwiKVxuICAgICAgICAgIGlmIGVyciA9PSBudWxsIHtcbiAgICAgICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QubWV0aG9kID0gdXJsWzBdXG4gICAgICAgICAgICAgIC5tZXRhZGF0YS5yZXF1ZXN0LnBhdGggPSB1cmxbMV1cbiAgICAgICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QucHJvdG9jb2wgPSB1cmxbMl1cbiAgICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiBlcnIgIT0gbnVsbCB7XG4gICAgICAgIGFib3J0XG4gICAgICB9XG4gICMgR290cnVlIGxvZ3MgYXJlIHN0cnVjdHVyZWQganNvbiBzdHJpbmdzIHdoaWNoIGZyb250ZW5kIHBhcnNlcyBkaXJlY3RseS4gQnV0IHdlIGtlZXAgbWV0YWRhdGEgZm9yIGNvbnNpc3RlbmN5LlxuICBhdXRoX2xvZ3M6XG4gICAgdHlwZTogcmVtYXBcbiAgICBpbnB1dHM6XG4gICAgICAtIHJvdXRlci5hdXRoXG4gICAgc291cmNlOiB8LVxuICAgICAgcGFyc2VkLCBlcnIgPSBwYXJzZV9qc29uKC5ldmVudF9tZXNzYWdlKVxuICAgICAgaWYgZXJyID09IG51bGwge1xuICAgICAgICAgIC5tZXRhZGF0YS50aW1lc3RhbXAgPSBwYXJzZWQudGltZVxuICAgICAgICAgIC5tZXRhZGF0YSA9IG1lcmdlISgubWV0YWRhdGEsIHBhcnNlZClcbiAgICAgIH1cbiAgIyBQb3N0Z1JFU1QgbG9ncyBhcmUgc3RydWN0dXJlZCBzbyB3ZSBzZXBhcmF0ZSB0aW1lc3RhbXAgZnJvbSBtZXNzYWdlIHVzaW5nIHJlZ2V4XG4gIHJlc3RfbG9nczpcbiAgICB0eXBlOiByZW1hcFxuICAgIGlucHV0czpcbiAgICAgIC0gcm91dGVyLnJlc3RcbiAgICBzb3VyY2U6IHwtXG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX3JlZ2V4KC5ldmVudF9tZXNzYWdlLCByJ14oP1A8dGltZT4uKik6ICg\/UDxtc2c+LiopJCcpXG4gICAgICBpZiBlcnIgPT0gbnVsbCB7XG4gICAgICAgICAgLmV2ZW50X21lc3NhZ2UgPSBwYXJzZWQubXNnXG4gICAgICAgICAgLnRpbWVzdGFtcCA9IHRvX3RpbWVzdGFtcCEocGFyc2VkLnRpbWUpXG4gICAgICAgICAgLm1ldGFkYXRhLmhvc3QgPSAucHJvamVjdFxuICAgICAgfVxuICAjIFJlYWx0aW1lIGxvZ3MgYXJlIHN0cnVjdHVyZWQgc28gd2UgcGFyc2UgdGhlIHNldmVyaXR5IGxldmVsIHVzaW5nIHJlZ2V4IChpZ25vcmUgdGltZSBiZWNhdXNlIGl0IGhhcyBubyBkYXRlKVxuICByZWFsdGltZV9sb2dzOlxuICAgIHR5cGU6IHJlbWFwXG4gICAgaW5wdXRzOlxuICAgICAgLSByb3V0ZXIucmVhbHRpbWVcbiAgICBzb3VyY2U6IHwtXG4gICAgICAubWV0YWRhdGEucHJvamVjdCA9IGRlbCgucHJvamVjdClcbiAgICAgIC5tZXRhZGF0YS5leHRlcm5hbF9pZCA9IC5tZXRhZGF0YS5wcm9qZWN0XG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX3JlZ2V4KC5ldmVudF9tZXNzYWdlLCByJ14oP1A8dGltZT5cXGQrOlxcZCs6XFxkK1xcLlxcZCspIFxcWyg\/UDxsZXZlbD5cXHcrKVxcXSAoP1A8bXNnPi4qKSQnKVxuICAgICAgaWYgZXJyID09IG51bGwge1xuICAgICAgICAgIC5ldmVudF9tZXNzYWdlID0gcGFyc2VkLm1zZ1xuICAgICAgICAgIC5tZXRhZGF0YS5sZXZlbCA9IHBhcnNlZC5sZXZlbFxuICAgICAgfVxuICAjIFN0b3JhZ2UgbG9ncyBtYXkgY29udGFpbiBqc29uIG9iamVjdHMgc28gd2UgcGFyc2UgdGhlbSBmb3IgY29tcGxldGVuZXNzXG4gIHN0b3JhZ2VfbG9nczpcbiAgICB0eXBlOiByZW1hcFxuICAgIGlucHV0czpcbiAgICAgIC0gcm91dGVyLnN0b3JhZ2VcbiAgICBzb3VyY2U6IHwtXG4gICAgICAubWV0YWRhdGEucHJvamVjdCA9IGRlbCgucHJvamVjdClcbiAgICAgIC5tZXRhZGF0YS50ZW5hbnRJZCA9IC5tZXRhZGF0YS5wcm9qZWN0XG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX2pzb24oLmV2ZW50X21lc3NhZ2UpXG4gICAgICBpZiBlcnIgPT0gbnVsbCB7XG4gICAgICAgICAgLmV2ZW50X21lc3NhZ2UgPSBwYXJzZWQubXNnXG4gICAgICAgICAgLm1ldGFkYXRhLmxldmVsID0gcGFyc2VkLmxldmVsXG4gICAgICAgICAgLm1ldGFkYXRhLnRpbWVzdGFtcCA9IHBhcnNlZC50aW1lXG4gICAgICAgICAgLm1ldGFkYXRhLmNvbnRleHRbMF0uaG9zdCA9IHBhcnNlZC5ob3N0bmFtZVxuICAgICAgICAgIC5tZXRhZGF0YS5jb250ZXh0WzBdLnBpZCA9IHBhcnNlZC5waWRcbiAgICAgIH1cbiAgIyBQb3N0Z3JlcyBsb2dzIHNvbWUgbWVzc2FnZXMgdG8gc3RkZXJyIHdoaWNoIHdlIG1hcCB0byB3YXJuaW5nIHNldmVyaXR5IGxldmVsXG4gIGRiX2xvZ3M6XG4gICAgdHlwZTogcmVtYXBcbiAgICBpbnB1dHM6XG4gICAgICAtIHJvdXRlci5kYlxuICAgIHNvdXJjZTogfC1cbiAgICAgIC5tZXRhZGF0YS5ob3N0ID0gXCJkYi1kZWZhdWx0XCJcbiAgICAgIC5tZXRhZGF0YS5wYXJzZWQudGltZXN0YW1wID0gLnRpbWVzdGFtcFxuXG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX3JlZ2V4KC5ldmVudF9tZXNzYWdlLCByJy4qKD9QPGxldmVsPklORk98Tk9USUNFfFdBUk5JTkd8RVJST1J8TE9HfEZBVEFMfFBBTklDPyk6LionLCBudW1lcmljX2dyb3VwczogdHJ1ZSlcblxuICAgICAgaWYgZXJyICE9IG51bGwgfHwgcGFyc2VkID09IG51bGwge1xuICAgICAgICAubWV0YWRhdGEucGFyc2VkLmVycm9yX3NldmVyaXR5ID0gXCJpbmZvXCJcbiAgICAgIH1cbiAgICAgIGlmIHBhcnNlZCAhPSBudWxsIHtcbiAgICAgIC5tZXRhZGF0YS5wYXJzZWQuZXJyb3Jfc2V2ZXJpdHkgPSBwYXJzZWQubGV2ZWxcbiAgICAgIH1cbiAgICAgIGlmIC5tZXRhZGF0YS5wYXJzZWQuZXJyb3Jfc2V2ZXJpdHkgPT0gXCJpbmZvXCIge1xuICAgICAgICAgIC5tZXRhZGF0YS5wYXJzZWQuZXJyb3Jfc2V2ZXJpdHkgPSBcImxvZ1wiXG4gICAgICB9XG4gICAgICAubWV0YWRhdGEucGFyc2VkLmVycm9yX3NldmVyaXR5ID0gdXBjYXNlISgubWV0YWRhdGEucGFyc2VkLmVycm9yX3NldmVyaXR5KVxuXG5zaW5rczpcbiAgbG9nZmxhcmVfYXV0aDpcbiAgICB0eXBlOiAnaHR0cCdcbiAgICBpbnB1dHM6XG4gICAgICAtIGF1dGhfbG9nc1xuICAgIGVuY29kaW5nOlxuICAgICAgY29kZWM6ICdqc29uJ1xuICAgIG1ldGhvZDogJ3Bvc3QnXG4gICAgcmVxdWVzdDpcbiAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMFxuICAgIHVyaTogJ2h0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMC9hcGkvbG9ncz9zb3VyY2VfbmFtZT1nb3RydWUubG9ncy5wcm9kJmFwaV9rZXk9JHtMT0dGTEFSRV9BUElfS0VZP0xPR0ZMQVJFX0FQSV9LRVkgaXMgcmVxdWlyZWR9J1xuICBsb2dmbGFyZV9yZWFsdGltZTpcbiAgICB0eXBlOiAnaHR0cCdcbiAgICBpbnB1dHM6XG4gICAgICAtIHJlYWx0aW1lX2xvZ3NcbiAgICBlbmNvZGluZzpcbiAgICAgIGNvZGVjOiAnanNvbidcbiAgICBtZXRob2Q6ICdwb3N0J1xuICAgIHJlcXVlc3Q6XG4gICAgICByZXRyeV9tYXhfZHVyYXRpb25fc2VjczogMTBcbiAgICB1cmk6ICdodHRwOi8vc3VwYWJhc2UtYW5hbHl0aWNzOjQwMDAvYXBpL2xvZ3M\/c291cmNlX25hbWU9cmVhbHRpbWUubG9ncy5wcm9kJmFwaV9rZXk9JHtMT0dGTEFSRV9BUElfS0VZP0xPR0ZMQVJFX0FQSV9LRVkgaXMgcmVxdWlyZWR9J1xuICBsb2dmbGFyZV9yZXN0OlxuICAgIHR5cGU6ICdodHRwJ1xuICAgIGlucHV0czpcbiAgICAgIC0gcmVzdF9sb2dzXG4gICAgZW5jb2Rpbmc6XG4gICAgICBjb2RlYzogJ2pzb24nXG4gICAgbWV0aG9kOiAncG9zdCdcbiAgICByZXF1ZXN0OlxuICAgICAgcmV0cnlfbWF4X2R1cmF0aW9uX3NlY3M6IDEwXG4gICAgdXJpOiAnaHR0cDovL3N1cGFiYXNlLWFuYWx5dGljczo0MDAwL2FwaS9sb2dzP3NvdXJjZV9uYW1lPXBvc3RnUkVTVC5sb2dzLnByb2QmYXBpX2tleT0ke0xPR0ZMQVJFX0FQSV9LRVk\/TE9HRkxBUkVfQVBJX0tFWSBpcyByZXF1aXJlZH0nXG4gIGxvZ2ZsYXJlX2RiOlxuICAgIHR5cGU6ICdodHRwJ1xuICAgIGlucHV0czpcbiAgICAgIC0gZGJfbG9nc1xuICAgIGVuY29kaW5nOlxuICAgICAgY29kZWM6ICdqc29uJ1xuICAgIG1ldGhvZDogJ3Bvc3QnXG4gICAgcmVxdWVzdDpcbiAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMFxuICAgICMgV2UgbXVzdCByb3V0ZSB0aGUgc2luayB0aHJvdWdoIGtvbmcgYmVjYXVzZSBpbmdlc3RpbmcgbG9ncyBiZWZvcmUgbG9nZmxhcmUgaXMgZnVsbHkgaW5pdGlhbGlzZWQgd2lsbFxuICAgICMgbGVhZCB0byBicm9rZW4gcXVlcmllcyBmcm9tIHN0dWRpby4gVGhpcyB3b3JrcyBieSB0aGUgYXNzdW1wdGlvbiB0aGF0IGNvbnRhaW5lcnMgYXJlIHN0YXJ0ZWQgaW4gdGhlXG4gICAgIyBmb2xsb3dpbmcgb3JkZXI6IHZlY3RvciA+IGRiID4gbG9nZmxhcmUgPiBrb25nXG4gICAgdXJpOiAnaHR0cDovL3N1cGFiYXNlLWtvbmc6ODAwMC9hbmFseXRpY3MvdjEvYXBpL2xvZ3M\/c291cmNlX25hbWU9cG9zdGdyZXMubG9ncyZhcGlfa2V5PSR7TE9HRkxBUkVfQVBJX0tFWT9MT0dGTEFSRV9BUElfS0VZIGlzIHJlcXVpcmVkfSdcbiAgbG9nZmxhcmVfZnVuY3Rpb25zOlxuICAgIHR5cGU6ICdodHRwJ1xuICAgIGlucHV0czpcbiAgICAgIC0gcm91dGVyLmZ1bmN0aW9uc1xuICAgIGVuY29kaW5nOlxuICAgICAgY29kZWM6ICdqc29uJ1xuICAgIG1ldGhvZDogJ3Bvc3QnXG4gICAgcmVxdWVzdDpcbiAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMFxuICAgIHVyaTogJ2h0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMC9hcGkvbG9ncz9zb3VyY2VfbmFtZT1kZW5vLXJlbGF5LWxvZ3MmYXBpX2tleT0ke0xPR0ZMQVJFX0FQSV9LRVk\/TE9HRkxBUkVfQVBJX0tFWSBpcyByZXF1aXJlZH0nXG4gIGxvZ2ZsYXJlX3N0b3JhZ2U6XG4gICAgdHlwZTogJ2h0dHAnXG4gICAgaW5wdXRzOlxuICAgICAgLSBzdG9yYWdlX2xvZ3NcbiAgICBlbmNvZGluZzpcbiAgICAgIGNvZGVjOiAnanNvbidcbiAgICBtZXRob2Q6ICdwb3N0J1xuICAgIHJlcXVlc3Q6XG4gICAgICByZXRyeV9tYXhfZHVyYXRpb25fc2VjczogMTBcbiAgICB1cmk6ICdodHRwOi8vc3VwYWJhc2UtYW5hbHl0aWNzOjQwMDAvYXBpL2xvZ3M\/c291cmNlX25hbWU9c3RvcmFnZS5sb2dzLnByb2QuMiZhcGlfa2V5PSR7TE9HRkxBUkVfQVBJX0tFWT9MT0dGTEFSRV9BUElfS0VZIGlzIHJlcXVpcmVkfSdcbiAgbG9nZmxhcmVfa29uZzpcbiAgICB0eXBlOiAnaHR0cCdcbiAgICBpbnB1dHM6XG4gICAgICAtIGtvbmdfbG9nc1xuICAgICAgLSBrb25nX2VyclxuICAgIGVuY29kaW5nOlxuICAgICAgY29kZWM6ICdqc29uJ1xuICAgIG1ldGhvZDogJ3Bvc3QnXG4gICAgcmVxdWVzdDpcbiAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMFxuICAgIHVyaTogJ2h0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMC9hcGkvbG9ncz9zb3VyY2VfbmFtZT1jbG91ZGZsYXJlLmxvZ3MucHJvZCZhcGlfa2V5PSR7TE9HRkxBUkVfQVBJX0tFWT9MT0dGTEFSRV9BUElfS0VZIGlzIHJlcXVpcmVkfSdcbiIKICAgICAgLSAnL3Zhci9ydW4vZG9ja2VyLnNvY2s6L3Zhci9ydW4vZG9ja2VyLnNvY2s6cm8nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTE9HRkxBUkVfQVBJX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTE9HRkxBUkV9JwogICAgY29tbWFuZDoKICAgICAgLSAnLS1jb25maWcnCiAgICAgIC0gZXRjL3ZlY3Rvci92ZWN0b3IueW1sCiAgc3VwYWJhc2UtcmVzdDoKICAgIGltYWdlOiAncG9zdGdyZXN0L3Bvc3RncmVzdDp2MTIuMC4xJwogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtZGI6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgc3VwYWJhc2UtYW5hbHl0aWNzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BHUlNUX0RCX1VSST1wb3N0Z3JlczovL2F1dGhlbnRpY2F0b3I6JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfUAke1BPU1RHUkVTX0hPU1Q6LXN1cGFiYXNlLWRifToke1BPU1RHUkVTX1BPUlQ6LTU0MzJ9LyR7UE9TVEdSRVNfREI6LXBvc3RncmVzfScKICAgICAgLSAnUEdSU1RfREJfU0NIRU1BUz0ke1BHUlNUX0RCX1NDSEVNQVM6LXB1YmxpY30nCiAgICAgIC0gUEdSU1RfREJfQU5PTl9ST0xFPWFub24KICAgICAgLSAnUEdSU1RfSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfScKICAgICAgLSBQR1JTVF9EQl9VU0VfTEVHQUNZX0dVQ1M9ZmFsc2UKICAgICAgLSAnUEdSU1RfQVBQX1NFVFRJTkdTX0pXVF9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX0pXVH0nCiAgICAgIC0gJ1BHUlNUX0FQUF9TRVRUSU5HU19KV1RfRVhQPSR7SldUX0VYUElSWTotMzYwMH0nCiAgICBjb21tYW5kOiBwb3N0Z3Jlc3QKICAgIGV4Y2x1ZGVfZnJvbV9oYzogdHJ1ZQogIHN1cGFiYXNlLWF1dGg6CiAgICBpbWFnZTogJ3N1cGFiYXNlL2dvdHJ1ZTp2Mi4xNTEuMCcKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWRiOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHN1cGFiYXNlLWFuYWx5dGljczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHdnZXQKICAgICAgICAtICctLW5vLXZlcmJvc2UnCiAgICAgICAgLSAnLS10cmllcz0xJwogICAgICAgIC0gJy0tc3BpZGVyJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6OTk5OS9oZWFsdGgnCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIGludGVydmFsOiA1cwogICAgICByZXRyaWVzOiAzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBHT1RSVUVfQVBJX0hPU1Q9MC4wLjAuMAogICAgICAtIEdPVFJVRV9BUElfUE9SVD05OTk5CiAgICAgIC0gJ0FQSV9FWFRFUk5BTF9VUkw9JHtBUElfRVhURVJOQUxfVVJMOi1odHRwOi8vc3VwYWJhc2Uta29uZzo4MDAwfScKICAgICAgLSBHT1RSVUVfREJfRFJJVkVSPXBvc3RncmVzCiAgICAgIC0gJ0dPVFJVRV9EQl9EQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly9zdXBhYmFzZV9hdXRoX2FkbWluOiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AJHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn06JHtQT1NUR1JFU19QT1JUOi01NDMyfS8ke1BPU1RHUkVTX0RCOi1wb3N0Z3Jlc30nCiAgICAgIC0gJ0dPVFJVRV9TSVRFX1VSTD0ke1NFUlZJQ0VfRlFETl9TVVBBQkFTRUtPTkd9JwogICAgICAtICdHT1RSVUVfVVJJX0FMTE9XX0xJU1Q9JHtBRERJVElPTkFMX1JFRElSRUNUX1VSTFN9JwogICAgICAtICdHT1RSVUVfRElTQUJMRV9TSUdOVVA9JHtESVNBQkxFX1NJR05VUDotZmFsc2V9JwogICAgICAtIEdPVFJVRV9KV1RfQURNSU5fUk9MRVM9c2VydmljZV9yb2xlCiAgICAgIC0gR09UUlVFX0pXVF9BVUQ9YXV0aGVudGljYXRlZAogICAgICAtIEdPVFJVRV9KV1RfREVGQVVMVF9HUk9VUF9OQU1FPWF1dGhlbnRpY2F0ZWQKICAgICAgLSAnR09UUlVFX0pXVF9FWFA9JHtKV1RfRVhQSVJZOi0zNjAwfScKICAgICAgLSAnR09UUlVFX0pXVF9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX0pXVH0nCiAgICAgIC0gJ0dPVFJVRV9FWFRFUk5BTF9FTUFJTF9FTkFCTEVEPSR7RU5BQkxFX0VNQUlMX1NJR05VUDotdHJ1ZX0nCiAgICAgIC0gJ0dPVFJVRV9FWFRFUk5BTF9BTk9OWU1PVVNfVVNFUlNfRU5BQkxFRD0ke0VOQUJMRV9BTk9OWU1PVVNfVVNFUlM6LWZhbHNlfScKICAgICAgLSAnR09UUlVFX01BSUxFUl9BVVRPQ09ORklSTT0ke0VOQUJMRV9FTUFJTF9BVVRPQ09ORklSTTotZmFsc2V9JwogICAgICAtICdHT1RSVUVfU01UUF9BRE1JTl9FTUFJTD0ke1NNVFBfQURNSU5fRU1BSUx9JwogICAgICAtICdHT1RSVUVfU01UUF9IT1NUPSR7U01UUF9IT1NUfScKICAgICAgLSAnR09UUlVFX1NNVFBfUE9SVD0ke1NNVFBfUE9SVDotNTg3fScKICAgICAgLSAnR09UUlVFX1NNVFBfVVNFUj0ke1NNVFBfVVNFUn0nCiAgICAgIC0gJ0dPVFJVRV9TTVRQX1BBU1M9JHtTTVRQX1BBU1N9JwogICAgICAtICdHT1RSVUVfU01UUF9TRU5ERVJfTkFNRT0ke1NNVFBfU0VOREVSX05BTUV9JwogICAgICAtICdHT1RSVUVfTUFJTEVSX1VSTFBBVEhTX0lOVklURT0ke01BSUxFUl9VUkxQQVRIU19JTlZJVEU6LS9hdXRoL3YxL3ZlcmlmeX0nCiAgICAgIC0gJ0dPVFJVRV9NQUlMRVJfVVJMUEFUSFNfQ09ORklSTUFUSU9OPSR7TUFJTEVSX1VSTFBBVEhTX0NPTkZJUk1BVElPTjotL2F1dGgvdjEvdmVyaWZ5fScKICAgICAgLSAnR09UUlVFX01BSUxFUl9VUkxQQVRIU19SRUNPVkVSWT0ke01BSUxFUl9VUkxQQVRIU19SRUNPVkVSWTotL2F1dGgvdjEvdmVyaWZ5fScKICAgICAgLSAnR09UUlVFX01BSUxFUl9VUkxQQVRIU19FTUFJTF9DSEFOR0U9JHtNQUlMRVJfVVJMUEFUSFNfRU1BSUxfQ0hBTkdFOi0vYXV0aC92MS92ZXJpZnl9JwogICAgICAtICdHT1RSVUVfTUFJTEVSX1RFTVBMQVRFU19JTlZJVEU9JHtNQUlMRVJfVEVNUExBVEVTX0lOVklURX0nCiAgICAgIC0gJ0dPVFJVRV9NQUlMRVJfVEVNUExBVEVTX0NPTkZJUk1BVElPTj0ke01BSUxFUl9URU1QTEFURVNfQ09ORklSTUFUSU9OfScKICAgICAgLSAnR09UUlVFX01BSUxFUl9URU1QTEFURVNfUkVDT1ZFUlk9JHtNQUlMRVJfVEVNUExBVEVTX1JFQ09WRVJZfScKICAgICAgLSAnR09UUlVFX01BSUxFUl9URU1QTEFURVNfTUFHSUNfTElOSz0ke01BSUxFUl9URU1QTEFURVNfTUFHSUNfTElOS30nCiAgICAgIC0gJ0dPVFJVRV9NQUlMRVJfVEVNUExBVEVTX0VNQUlMX0NIQU5HRT0ke01BSUxFUl9URU1QTEFURVNfRU1BSUxfQ0hBTkdFfScKICAgICAgLSAnR09UUlVFX01BSUxFUl9TVUJKRUNUU19DT05GSVJNQVRJT049JHtNQUlMRVJfU1VCSkVDVFNfQ09ORklSTUFUSU9OfScKICAgICAgLSAnR09UUlVFX01BSUxFUl9TVUJKRUNUU19SRUNPVkVSWT0ke01BSUxFUl9TVUJKRUNUU19SRUNPVkVSWX0nCiAgICAgIC0gJ0dPVFJVRV9NQUlMRVJfU1VCSkVDVFNfTUFHSUNfTElOSz0ke01BSUxFUl9TVUJKRUNUU19NQUdJQ19MSU5LfScKICAgICAgLSAnR09UUlVFX01BSUxFUl9TVUJKRUNUU19FTUFJTF9DSEFOR0U9JHtNQUlMRVJfU1VCSkVDVFNfRU1BSUxfQ0hBTkdFfScKICAgICAgLSAnR09UUlVFX01BSUxFUl9TVUJKRUNUU19JTlZJVEU9JHtNQUlMRVJfU1VCSkVDVFNfSU5WSVRFfScKICAgICAgLSAnR09UUlVFX0VYVEVSTkFMX1BIT05FX0VOQUJMRUQ9JHtFTkFCTEVfUEhPTkVfU0lHTlVQOi10cnVlfScKICAgICAgLSAnR09UUlVFX1NNU19BVVRPQ09ORklSTT0ke0VOQUJMRV9QSE9ORV9BVVRPQ09ORklSTTotdHJ1ZX0nCiAgcmVhbHRpbWUtZGV2OgogICAgaW1hZ2U6ICdzdXBhYmFzZS9yZWFsdGltZTp2Mi4yOC4zMicKICAgIGNvbnRhaW5lcl9uYW1lOiByZWFsdGltZS1kZXYuc3VwYWJhc2UtcmVhbHRpbWUKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWRiOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHN1cGFiYXNlLWFuYWx5dGljczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctc1NmTCcKICAgICAgICAtICctLWhlYWQnCiAgICAgICAgLSAnLW8nCiAgICAgICAgLSAvZGV2L251bGwKICAgICAgICAtICctSCcKICAgICAgICAtICdBdXRob3JpemF0aW9uOiBCZWFyZXIgJHtTRVJWSUNFX1NVUEFCQVNFQU5PTl9LRVl9JwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6NDAwMC9hcGkvdGVuYW50cy9yZWFsdGltZS1kZXYvaGVhbHRoJwogICAgICB0aW1lb3V0OiA1cwogICAgICBpbnRlcnZhbDogNXMKICAgICAgcmV0cmllczogMwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9SVD00MDAwCiAgICAgIC0gJ0RCX0hPU1Q9JHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn0nCiAgICAgIC0gJ0RCX1BPUlQ9JHtQT1NUR1JFU19QT1JUOi01NDMyfScKICAgICAgLSBEQl9VU0VSPXN1cGFiYXNlX2FkbWluCiAgICAgIC0gJ0RCX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gJ0RCX05BTUU9JHtQT1NUR1JFU19EQjotcG9zdGdyZXN9JwogICAgICAtICdEQl9BRlRFUl9DT05ORUNUX1FVRVJZPVNFVCBzZWFyY2hfcGF0aCBUTyBfcmVhbHRpbWUnCiAgICAgIC0gREJfRU5DX0tFWT1zdXBhYmFzZXJlYWx0aW1lCiAgICAgIC0gJ0FQSV9KV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF9KV1R9JwogICAgICAtIEZMWV9BTExPQ19JRD1mbHkxMjMKICAgICAgLSBGTFlfQVBQX05BTUU9cmVhbHRpbWUKICAgICAgLSAnU0VDUkVUX0tFWV9CQVNFPSR7U0VDUkVUX1BBU1NXT1JEX1JFQUxUSU1FfScKICAgICAgLSAnRVJMX0FGTEFHUz0tcHJvdG9fZGlzdCBpbmV0X3RjcCcKICAgICAgLSBFTkFCTEVfVEFJTFNDQUxFPWZhbHNlCiAgICAgIC0gIkROU19OT0RFUz0nJyIKICAgIGNvbW1hbmQ6ICJzaCAtYyBcIi9hcHAvYmluL21pZ3JhdGUgJiYgL2FwcC9iaW4vcmVhbHRpbWUgZXZhbCAnUmVhbHRpbWUuUmVsZWFzZS5zZWVkcyhSZWFsdGltZS5SZXBvKScgJiYgL2FwcC9iaW4vc2VydmVyXCJcbiIKICBzdXBhYmFzZS1taW5pbzoKICAgIGltYWdlOiBtaW5pby9taW5pbwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01JTklPX1JPT1RfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ01JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfScKICAgIGNvbW1hbmQ6ICdzZXJ2ZXIgLS1jb25zb2xlLWFkZHJlc3MgIjo5MDAxIiAvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAnc2xlZXAgNSAmJiBleGl0IDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogNQogICAgdm9sdW1lczoKICAgICAgLSAnLi92b2x1bWVzL3N0b3JhZ2U6L2RhdGEnCiAgbWluaW8tY3JlYXRlYnVja2V0OgogICAgaW1hZ2U6IG1pbmlvL21jCiAgICByZXN0YXJ0OiAnbm8nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfScKICAgICAgLSAnTUlOSU9fUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudHJ5cG9pbnQ6CiAgICAgIC0gL2VudHJ5cG9pbnQuc2gKICAgIHZvbHVtZXM6CiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2VudHJ5cG9pbnQuc2gKICAgICAgICB0YXJnZXQ6IC9lbnRyeXBvaW50LnNoCiAgICAgICAgY29udGVudDogIiMhL2Jpbi9zaFxuL3Vzci9iaW4vbWMgYWxpYXMgc2V0IHN1cGFiYXNlLW1pbmlvIGh0dHA6Ly9zdXBhYmFzZS1taW5pbzo5MDAwICR7TUlOSU9fUk9PVF9VU0VSfSAke01JTklPX1JPT1RfUEFTU1dPUkR9O1xuL3Vzci9iaW4vbWMgbWIgLS1pZ25vcmUtZXhpc3Rpbmcgc3VwYWJhc2UtbWluaW8vc3R1YjtcbmV4aXQgMFxuIgogIHN1cGFiYXNlLXN0b3JhZ2U6CiAgICBpbWFnZTogJ3N1cGFiYXNlL3N0b3JhZ2UtYXBpOnYxLjAuNicKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWRiOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHN1cGFiYXNlLXJlc3Q6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgICAgaW1ncHJveHk6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLS1uby12ZXJib3NlJwogICAgICAgIC0gJy0tdHJpZXM9MScKICAgICAgICAtICctLXNwaWRlcicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjUwMDAvc3RhdHVzJwogICAgICB0aW1lb3V0OiA1cwogICAgICBpbnRlcnZhbDogNXMKICAgICAgcmV0cmllczogMwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVkVSX1BPUlQ9NTAwMAogICAgICAtIFNFUlZFUl9SRUdJT049bG9jYWwKICAgICAgLSBNVUxUSV9URU5BTlQ9ZmFsc2UKICAgICAgLSAnQVVUSF9KV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF9KV1R9JwogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly9zdXBhYmFzZV9zdG9yYWdlX2FkbWluOiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AJHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn06JHtQT1NUR1JFU19QT1JUOi01NDMyfS8ke1BPU1RHUkVTX0RCOi1wb3N0Z3Jlc30nCiAgICAgIC0gREJfSU5TVEFMTF9ST0xFUz1mYWxzZQogICAgICAtIFNUT1JBR0VfQkFDS0VORD1zMwogICAgICAtIFNUT1JBR0VfUzNfQlVDS0VUPXN0dWIKICAgICAgLSAnU1RPUkFHRV9TM19FTkRQT0lOVD1odHRwOi8vc3VwYWJhc2UtbWluaW86OTAwMCcKICAgICAgLSBTVE9SQUdFX1MzX0ZPUkNFX1BBVEhfU1RZTEU9dHJ1ZQogICAgICAtIFNUT1JBR0VfUzNfUkVHSU9OPXVzLWVhc3QtMQogICAgICAtICdBV1NfQUNDRVNTX0tFWV9JRD0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ0FXU19TRUNSRVRfQUNDRVNTX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgICAtIFVQTE9BRF9GSUxFX1NJWkVfTElNSVQ9NTI0Mjg4MDAwCiAgICAgIC0gVVBMT0FEX0ZJTEVfU0laRV9MSU1JVF9TVEFOREFSRD01MjQyODgwMDAKICAgICAgLSBVUExPQURfU0lHTkVEX1VSTF9FWFBJUkFUSU9OX1RJTUU9MTIwCiAgICAgIC0gVFVTX1VSTF9QQVRIPS91cGxvYWQvcmVzdW1hYmxlCiAgICAgIC0gVFVTX01BWF9TSVpFPTM2MDAwMDAKICAgICAgLSBJTUFHRV9UUkFOU0ZPUk1BVElPTl9FTkFCTEVEPXRydWUKICAgICAgLSAnSU1HUFJPWFlfVVJMPWh0dHA6Ly9pbWdwcm94eTo4MDgwJwogICAgICAtIElNR1BST1hZX1JFUVVFU1RfVElNRU9VVD0xNQogICAgICAtIERBVEFCQVNFX1NFQVJDSF9QQVRIPXN0b3JhZ2UKICAgIHZvbHVtZXM6CiAgICAgIC0gJy4vdm9sdW1lcy9zdG9yYWdlOi92YXIvbGliL3N0b3JhZ2UnCiAgaW1ncHJveHk6CiAgICBpbWFnZTogJ2RhcnRoc2ltL2ltZ3Byb3h5OnYzLjguMCcKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBpbWdwcm94eQogICAgICAgIC0gaGVhbHRoCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIGludGVydmFsOiA1cwogICAgICByZXRyaWVzOiAzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBJTUdQUk9YWV9MT0NBTF9GSUxFU1lTVEVNX1JPT1Q9LwogICAgICAtIElNR1BST1hZX1VTRV9FVEFHPXRydWUKICAgICAgLSAnSU1HUFJPWFlfRU5BQkxFX1dFQlBfREVURUNUSU9OPSR7SU1HUFJPWFlfRU5BQkxFX1dFQlBfREVURUNUSU9OOi10cnVlfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJy4vdm9sdW1lcy9zdG9yYWdlOi92YXIvbGliL3N0b3JhZ2UnCiAgc3VwYWJhc2UtbWV0YToKICAgIGltYWdlOiAnc3VwYWJhc2UvcG9zdGdyZXMtbWV0YTp2MC44MC4wJwogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtZGI6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgc3VwYWJhc2UtYW5hbHl0aWNzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQR19NRVRBX1BPUlQ9ODA4MAogICAgICAtICdQR19NRVRBX0RCX0hPU1Q9JHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn0nCiAgICAgIC0gJ1BHX01FVEFfREJfUE9SVD0ke1BPU1RHUkVTX1BPUlQ6LTU0MzJ9JwogICAgICAtICdQR19NRVRBX0RCX05BTUU9JHtQT1NUR1JFU19EQjotcG9zdGdyZXN9JwogICAgICAtIFBHX01FVEFfREJfVVNFUj1zdXBhYmFzZV9hZG1pbgogICAgICAtICdQR19NRVRBX0RCX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgc3VwYWJhc2UtZWRnZS1mdW5jdGlvbnM6CiAgICBpbWFnZTogJ3N1cGFiYXNlL2VkZ2UtcnVudGltZTp2MS41My4zJwogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtYW5hbHl0aWNzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gZWNobwogICAgICAgIC0gJ0VkZ2UgRnVuY3Rpb25zIGlzIGhlYWx0aHknCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIGludGVydmFsOiA1cwogICAgICByZXRyaWVzOiAzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfScKICAgICAgLSAnU1VQQUJBU0VfVVJMPWh0dHA6Ly9zdXBhYmFzZS1rb25nOjgwMDAnCiAgICAgIC0gJ1NVUEFCQVNFX0FOT05fS0VZPSR7U0VSVklDRV9TVVBBQkFTRUFOT05fS0VZfScKICAgICAgLSAnU1VQQUJBU0VfU0VSVklDRV9ST0xFX0tFWT0ke1NFUlZJQ0VfU1VQQUJBU0VTRVJWSUNFX0tFWX0nCiAgICAgIC0gJ1NVUEFCQVNFX0RCX1VSTD1wb3N0Z3Jlc3FsOi8vcG9zdGdyZXM6JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfUAke1BPU1RHUkVTX0hPU1Q6LXN1cGFiYXNlLWRifToke1BPU1RHUkVTX1BPUlQ6LTU0MzJ9LyR7UE9TVEdSRVNfREI6LXBvc3RncmVzfScKICAgICAgLSAnVkVSSUZZX0pXVD0ke0ZVTkNUSU9OU19WRVJJRllfSldUOi1mYWxzZX0nCiAgICB2b2x1bWVzOgogICAgICAtICcuL3ZvbHVtZXMvZnVuY3Rpb25zOi9ob21lL2Rlbm8vZnVuY3Rpb25zJwogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi92b2x1bWVzL2Z1bmN0aW9ucy9tYWluL2luZGV4LnRzCiAgICAgICAgdGFyZ2V0OiAvaG9tZS9kZW5vL2Z1bmN0aW9ucy9tYWluL2luZGV4LnRzCiAgICAgICAgY29udGVudDogImltcG9ydCB7IHNlcnZlIH0gZnJvbSAnaHR0cHM6Ly9kZW5vLmxhbmQvc3RkQDAuMTMxLjAvaHR0cC9zZXJ2ZXIudHMnXG5pbXBvcnQgKiBhcyBqb3NlIGZyb20gJ2h0dHBzOi8vZGVuby5sYW5kL3gvam9zZUB2NC4xNC40L2luZGV4LnRzJ1xuXG5jb25zb2xlLmxvZygnbWFpbiBmdW5jdGlvbiBzdGFydGVkJylcblxuY29uc3QgSldUX1NFQ1JFVCA9IERlbm8uZW52LmdldCgnSldUX1NFQ1JFVCcpXG5jb25zdCBWRVJJRllfSldUID0gRGVuby5lbnYuZ2V0KCdWRVJJRllfSldUJykgPT09ICd0cnVlJ1xuXG5mdW5jdGlvbiBnZXRBdXRoVG9rZW4ocmVxOiBSZXF1ZXN0KSB7XG4gIGNvbnN0IGF1dGhIZWFkZXIgPSByZXEuaGVhZGVycy5nZXQoJ2F1dGhvcml6YXRpb24nKVxuICBpZiAoIWF1dGhIZWFkZXIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ01pc3NpbmcgYXV0aG9yaXphdGlvbiBoZWFkZXInKVxuICB9XG4gIGNvbnN0IFtiZWFyZXIsIHRva2VuXSA9IGF1dGhIZWFkZXIuc3BsaXQoJyAnKVxuICBpZiAoYmVhcmVyICE9PSAnQmVhcmVyJykge1xuICAgIHRocm93IG5ldyBFcnJvcihgQXV0aCBoZWFkZXIgaXMgbm90ICdCZWFyZXIge3Rva2VufSdgKVxuICB9XG4gIHJldHVybiB0b2tlblxufVxuXG5hc3luYyBmdW5jdGlvbiB2ZXJpZnlKV1Qoand0OiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgY29uc3QgZW5jb2RlciA9IG5ldyBUZXh0RW5jb2RlcigpXG4gIGNvbnN0IHNlY3JldEtleSA9IGVuY29kZXIuZW5jb2RlKEpXVF9TRUNSRVQpXG4gIHRyeSB7XG4gICAgYXdhaXQgam9zZS5qd3RWZXJpZnkoand0LCBzZWNyZXRLZXkpXG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGNvbnNvbGUuZXJyb3IoZXJyKVxuICAgIHJldHVybiBmYWxzZVxuICB9XG4gIHJldHVybiB0cnVlXG59XG5cbnNlcnZlKGFzeW5jIChyZXE6IFJlcXVlc3QpID0+IHtcbiAgaWYgKHJlcS5tZXRob2QgIT09ICdPUFRJT05TJyAmJiBWRVJJRllfSldUKSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHRva2VuID0gZ2V0QXV0aFRva2VuKHJlcSlcbiAgICAgIGNvbnN0IGlzVmFsaWRKV1QgPSBhd2FpdCB2ZXJpZnlKV1QodG9rZW4pXG5cbiAgICAgIGlmICghaXNWYWxpZEpXVCkge1xuICAgICAgICByZXR1cm4gbmV3IFJlc3BvbnNlKEpTT04uc3RyaW5naWZ5KHsgbXNnOiAnSW52YWxpZCBKV1QnIH0pLCB7XG4gICAgICAgICAgc3RhdHVzOiA0MDEsXG4gICAgICAgICAgaGVhZGVyczogeyAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nIH0sXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY29uc29sZS5lcnJvcihlKVxuICAgICAgcmV0dXJuIG5ldyBSZXNwb25zZShKU09OLnN0cmluZ2lmeSh7IG1zZzogZS50b1N0cmluZygpIH0pLCB7XG4gICAgICAgIHN0YXR1czogNDAxLFxuICAgICAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSxcbiAgICAgIH0pXG4gICAgfVxuICB9XG5cbiAgY29uc3QgdXJsID0gbmV3IFVSTChyZXEudXJsKVxuICBjb25zdCB7IHBhdGhuYW1lIH0gPSB1cmxcbiAgY29uc3QgcGF0aF9wYXJ0cyA9IHBhdGhuYW1lLnNwbGl0KCcvJylcbiAgY29uc3Qgc2VydmljZV9uYW1lID0gcGF0aF9wYXJ0c1sxXVxuXG4gIGlmICghc2VydmljZV9uYW1lIHx8IHNlcnZpY2VfbmFtZSA9PT0gJycpIHtcbiAgICBjb25zdCBlcnJvciA9IHsgbXNnOiAnbWlzc2luZyBmdW5jdGlvbiBuYW1lIGluIHJlcXVlc3QnIH1cbiAgICByZXR1cm4gbmV3IFJlc3BvbnNlKEpTT04uc3RyaW5naWZ5KGVycm9yKSwge1xuICAgICAgc3RhdHVzOiA0MDAsXG4gICAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSxcbiAgICB9KVxuICB9XG5cbiAgY29uc3Qgc2VydmljZVBhdGggPSBgL2hvbWUvZGVuby9mdW5jdGlvbnMvJHtzZXJ2aWNlX25hbWV9YFxuICBjb25zb2xlLmVycm9yKGBzZXJ2aW5nIHRoZSByZXF1ZXN0IHdpdGggJHtzZXJ2aWNlUGF0aH1gKVxuXG4gIGNvbnN0IG1lbW9yeUxpbWl0TWIgPSAxNTBcbiAgY29uc3Qgd29ya2VyVGltZW91dE1zID0gMSAqIDYwICogMTAwMFxuICBjb25zdCBub01vZHVsZUNhY2hlID0gZmFsc2VcbiAgY29uc3QgaW1wb3J0TWFwUGF0aCA9IG51bGxcbiAgY29uc3QgZW52VmFyc09iaiA9IERlbm8uZW52LnRvT2JqZWN0KClcbiAgY29uc3QgZW52VmFycyA9IE9iamVjdC5rZXlzKGVudlZhcnNPYmopLm1hcCgoaykgPT4gW2ssIGVudlZhcnNPYmpba11dKVxuXG4gIHRyeSB7XG4gICAgY29uc3Qgd29ya2VyID0gYXdhaXQgRWRnZVJ1bnRpbWUudXNlcldvcmtlcnMuY3JlYXRlKHtcbiAgICAgIHNlcnZpY2VQYXRoLFxuICAgICAgbWVtb3J5TGltaXRNYixcbiAgICAgIHdvcmtlclRpbWVvdXRNcyxcbiAgICAgIG5vTW9kdWxlQ2FjaGUsXG4gICAgICBpbXBvcnRNYXBQYXRoLFxuICAgICAgZW52VmFycyxcbiAgICB9KVxuICAgIHJldHVybiBhd2FpdCB3b3JrZXIuZmV0Y2gocmVxKVxuICB9IGNhdGNoIChlKSB7XG4gICAgY29uc3QgZXJyb3IgPSB7IG1zZzogZS50b1N0cmluZygpIH1cbiAgICByZXR1cm4gbmV3IFJlc3BvbnNlKEpTT04uc3RyaW5naWZ5KGVycm9yKSwge1xuICAgICAgc3RhdHVzOiA1MDAsXG4gICAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSxcbiAgICB9KVxuICB9XG59KSIKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vdm9sdW1lcy9mdW5jdGlvbnMvaGVsbG8vaW5kZXgudHMKICAgICAgICB0YXJnZXQ6IC9ob21lL2Rlbm8vZnVuY3Rpb25zL2hlbGxvL2luZGV4LnRzCiAgICAgICAgY29udGVudDogIi8vIEZvbGxvdyB0aGlzIHNldHVwIGd1aWRlIHRvIGludGVncmF0ZSB0aGUgRGVubyBsYW5ndWFnZSBzZXJ2ZXIgd2l0aCB5b3VyIGVkaXRvcjpcbi8vIGh0dHBzOi8vZGVuby5sYW5kL21hbnVhbC9nZXR0aW5nX3N0YXJ0ZWQvc2V0dXBfeW91cl9lbnZpcm9ubWVudFxuLy8gVGhpcyBlbmFibGVzIGF1dG9jb21wbGV0ZSwgZ28gdG8gZGVmaW5pdGlvbiwgZXRjLlxuXG5pbXBvcnQgeyBzZXJ2ZSB9IGZyb20gXCJodHRwczovL2Rlbm8ubGFuZC9zdGRAMC4xNzcuMS9odHRwL3NlcnZlci50c1wiXG5cbnNlcnZlKGFzeW5jICgpID0+IHtcbiAgcmV0dXJuIG5ldyBSZXNwb25zZShcbiAgICBgXCJIZWxsbyBmcm9tIEVkZ2UgRnVuY3Rpb25zIVwiYCxcbiAgICB7IGhlYWRlcnM6IHsgXCJDb250ZW50LVR5cGVcIjogXCJhcHBsaWNhdGlvbi9qc29uXCIgfSB9LFxuICApXG59KVxuXG4vLyBUbyBpbnZva2U6XG4vLyBjdXJsICdodHRwOi8vbG9jYWxob3N0OjxLT05HX0hUVFBfUE9SVD4vZnVuY3Rpb25zL3YxL2hlbGxvJyBcXFxuLy8gICAtLWhlYWRlciAnQXV0aG9yaXphdGlvbjogQmVhcmVyIDxhbm9uL3NlcnZpY2Vfcm9sZSBBUEkga2V5PidcbiIKICAgIGNvbW1hbmQ6CiAgICAgIC0gc3RhcnQKICAgICAgLSAnLS1tYWluLXNlcnZpY2UnCiAgICAgIC0gL2hvbWUvZGVuby9mdW5jdGlvbnMvbWFpbgo=","tags":["firebase","alternative","open-source"],"logo":"svgs\/supabase.svg","minversion":"4.0.0-beta.228","port":"8000"},"syncthing":{"documentation":"https:\/\/syncthing.net\/?utm_source=coolify.io","slogan":"Syncthing synchronizes files between two or more computers in real time.","compose":"c2VydmljZXM6CiAgc3luY3RoaW5nOgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL3N5bmN0aGluZzpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fU1lOQ1RISU5HXzgzODQKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdGMvVVRDCiAgICB2b2x1bWVzOgogICAgICAtICdzeW5jdGhpbmctY29uZmlnOi9jb25maWcnCiAgICAgIC0gJ3N5bmN0aGluZy1kYXRhMTovZGF0YTEnCiAgICAgIC0gJ3N5bmN0aGluZy1kYXRhMjovZGF0YTInCiAgICBwb3J0czoKICAgICAgLSAnMjIwMDA6MjIwMDAvdGNwJwogICAgICAtICcyMjAwMDoyMjAwMC91ZHAnCiAgICAgIC0gJzIxMDI3OjIxMDI3L3VkcCcK","tags":["filestorage","data","synchronization"],"logo":"svgs\/syncthing.svg","minversion":"0.0.0","port":"8384"},"tolgee":{"documentation":"https:\/\/tolgee.io\/?utm_source=coolify.io","slogan":"Tolgee is a localization management platform for developers and translators.","compose":"c2VydmljZXM6CiAgdG9sZ2VlOgogICAgaW1hZ2U6IHRvbGdlZS90b2xnZWUKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9UT0xHRUVfODA4MAogICAgICAtIFRPTEdFRV9BVVRIRU5USUNBVElPTl9FTkFCTEVEPXRydWUKICAgICAgLSBUT0xHRUVfQVVUSEVOVElDQVRJT05fSU5JVElBTF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9UT0xHRUUKICAgICAgLSBUT0xHRUVfQVVUSEVOVElDQVRJT05fSU5JVElBTF9VU0VSTkFNRT1hZG1pbgogICAgICAtIFRPTEdFRV9BVVRIRU5USUNBVElPTl9KV1RfU0VDUkVUPSRTRVJWSUNFX1BBU1NXT1JEX0pXVAogICAgICAtIFRPTEdFRV9QT1NUR1JFU19BVVRPU1RBUlRfRU5BQkxFRD1mYWxzZQogICAgICAtICdTUFJJTkdfREFUQVNPVVJDRV9VUkw9amRiYzpwb3N0Z3Jlc3FsOi8vcG9zdGdyZXNxbDo1NDMyLyR7UE9TVEdSRVNfREI6LXRvbGdlZX0nCiAgICAgIC0gJ1NQUklOR19EQVRBU09VUkNFX1VTRVJOQU1FPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdTUFJJTkdfREFUQVNPVVJDRV9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICB2b2x1bWVzOgogICAgICAtICd0b2xnZWUtZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLXEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MDgwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3Jlc3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAndG9sZ2VlLXBvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTUUxfREFUQUJBU0U6LXRvbGdlZX0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["localization","translation","management","platform"],"logo":"svgs\/tolgee.svg","minversion":"0.0.0","port":"8080"},"trigger-with-external-database":{"documentation":"https:\/\/trigger.dev?utm_source=coolify.io","slogan":"The open source Background Jobs framework for TypeScript","compose":"c2VydmljZXM6CiAgdHJpZ2dlcjoKICAgIGltYWdlOiAnZ2hjci5pby90cmlnZ2VyZG90ZGV2L3RyaWdnZXIuZGV2OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9UUklHR0VSXzMwMDAKICAgICAgLSBMT0dJTl9PUklHSU49JFNFUlZJQ0VfRlFETl9UUklHR0VSCiAgICAgIC0gQVBQX09SSUdJTj0kU0VSVklDRV9GUUROX1RSSUdHRVIKICAgICAgLSBNQUdJQ19MSU5LX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9NQUdJQwogICAgICAtIEVOQ1JZUFRJT05fS0VZPSRTRVJWSUNFX1BBU1NXT1JEXzY0X0VOQ1JZUFRJT04KICAgICAgLSBTRVNTSU9OX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9TRVNTSU9OCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD0ke0RBVEFCQVNFX1VSTH0nCiAgICAgIC0gJ0RJUkVDVF9VUkw9JHtEQVRBQkFTRV9VUkx9JwogICAgICAtIFJVTlRJTUVfUExBVEZPUk09ZG9ja2VyLWNvbXBvc2UKICAgICAgLSBOT0RFX0VOVj1wcm9kdWN0aW9uCiAgICAgIC0gJ0FVVEhfR0lUSFVCX0NMSUVOVF9JRD0ke0FVVEhfR0lUSFVCX0NMSUVOVF9JRH0nCiAgICAgIC0gJ0FVVEhfR0lUSFVCX0NMSUVOVF9TRUNSRVQ9JHtBVVRIX0dJVEhVQl9DTElFTlRfU0VDUkVUfScKICAgICAgLSAnUkVTRU5EX0FQSV9LRVk9JHtSRVNFTkRfQVBJX0tFWX0nCiAgICAgIC0gJ0ZST01fRU1BSUw9JHtGUk9NX0VNQUlMfScKICAgICAgLSAnUkVQTFlfVE9fRU1BSUw9JHtSRVBMWV9UT19FTUFJTH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIE5PTkUK","tags":["trigger.dev","background jobs","typescript","trigger","jobs","cron","scheduler"],"logo":"svgs\/trigger.png","minversion":"0.0.0","port":"3000"},"trigger":{"documentation":"https:\/\/trigger.dev?utm_source=coolify.io","slogan":"The open source Background Jobs framework for TypeScript","compose":"c2VydmljZXM6CiAgdHJpZ2dlcjoKICAgIGltYWdlOiAnZ2hjci5pby90cmlnZ2VyZG90ZGV2L3RyaWdnZXIuZGV2OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9UUklHR0VSXzMwMDAKICAgICAgLSBMT0dJTl9PUklHSU49JFNFUlZJQ0VfRlFETl9UUklHR0VSCiAgICAgIC0gQVBQX09SSUdJTj0kU0VSVklDRV9GUUROX1RSSUdHRVIKICAgICAgLSBNQUdJQ19MSU5LX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9NQUdJQwogICAgICAtIEVOQ1JZUFRJT05fS0VZPSRTRVJWSUNFX1BBU1NXT1JEXzY0X0VOQ1JZUFRJT04KICAgICAgLSBTRVNTSU9OX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9TRVNTSU9OCiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotdHJpZ2dlcn0nCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzcWw6NTQzMi8kUE9TVEdSRVNfREInCiAgICAgIC0gJ0RJUkVDVF9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzcWw6NTQzMi8kUE9TVEdSRVNfREInCiAgICAgIC0gUlVOVElNRV9QTEFURk9STT1kb2NrZXItY29tcG9zZQogICAgICAtIE5PREVfRU5WPXByb2R1Y3Rpb24KICAgICAgLSAnQVVUSF9HSVRIVUJfQ0xJRU5UX0lEPSR7QVVUSF9HSVRIVUJfQ0xJRU5UX0lEfScKICAgICAgLSAnQVVUSF9HSVRIVUJfQ0xJRU5UX1NFQ1JFVD0ke0FVVEhfR0lUSFVCX0NMSUVOVF9TRUNSRVR9JwogICAgICAtICdSRVNFTkRfQVBJX0tFWT0ke1JFU0VORF9BUElfS0VZfScKICAgICAgLSAnRlJPTV9FTUFJTD0ke0ZST01fRU1BSUx9JwogICAgICAtICdSRVBMWV9UT19FTUFJTD0ke1JFUExZX1RPX0VNQUlMfScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gTk9ORQogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi10cmlnZ2VyfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["trigger.dev","background jobs","typescript","trigger","jobs","cron","scheduler"],"logo":"svgs\/trigger.png","minversion":"0.0.0","port":"3000"},"twenty":{"documentation":"https:\/\/docs.twenty.com?utm_source=coolify.io","slogan":"Twenty is a CRM designed to fit your unique business needs.","compose":"c2VydmljZXM6CiAgdHdlbnR5OgogICAgaW1hZ2U6ICd0d2VudHljcm0vdHdlbnR5OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9UUklHR0VSXzMwMDAKICAgICAgLSBTRVJWRVJfVVJMPSRTRVJWSUNFX0ZRRE5fVFdFTlRZCiAgICAgIC0gRlJPTlRfQkFTRV9VUkw9JFNFUlZJQ0VfRlFETl9UV0VOVFkKICAgICAgLSBFTkFCTEVfREJfTUlHUkFUSU9OUz10cnVlCiAgICAgIC0gU0lHTl9JTl9QUkVGSUxMRUQ9ZmFsc2UKICAgICAgLSAnU1RPUkFHRV9UWVBFPSR7U1RPUkFHRV9UWVBFOi1sb2NhbH0nCiAgICAgIC0gU1RPUkFHRV9TM19SRUdJT049JFNUT1JBR0VfUzNfUkVHSU9OCiAgICAgIC0gU1RPUkFHRV9TM19OQU1FPSRTVE9SQUdFX1MzX05BTUUKICAgICAgLSBTVE9SQUdFX1MzX0VORFBPSU5UPSRTVE9SQUdFX1MzX0VORFBPSU5UCiAgICAgIC0gQUNDRVNTX1RPS0VOX1NFQ1JFVD0kU0VSVklDRV9CQVNFNjRfMzJfQUNDRVNTCiAgICAgIC0gTE9HSU5fVE9LRU5fU0VDUkVUPSRTRVJWSUNFX0JBU0U2NF8zMl9MT0dJTgogICAgICAtIFJFRlJFU0hfVE9LRU5fU0VDUkVUPSRTRVJWSUNFX0JBU0U2NF8zMl9SRUZSRVNICiAgICAgIC0gRklMRV9UT0tFTl9TRUNSRVQ9JFNFUlZJQ0VfQkFTRTY0XzMyX0ZJTEUKICAgICAgLSBQT1NUR1JFU19BRE1JTl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdQR19EQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly9wb3N0Z3JlczokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0Bwb3N0Z3Jlczo1NDMyL2RlZmF1bHQnCiAgICAgIC0gRU1BSUxfRlJPTV9BRERSRVNTPSRFTUFJTF9GUk9NX0FERFJFU1MKICAgICAgLSBFTUFJTF9GUk9NX05BTUU9JEVNQUlMX0ZST01fTkFNRQogICAgICAtIEVNQUlMX1NZU1RFTV9BRERSRVNTPSRFTUFJTF9TWVNURU1fQUREUkVTUwogICAgICAtICdFTUFJTF9EUklWRVI9JHtFTUFJTF9EUklWRVI6LWxvZ2dlcn0nCiAgICAgIC0gRU1BSUxfU01UUF9IT1NUPSRFTUFJTF9TTVRQX0hPU1QKICAgICAgLSBFTUFJTF9TTVRQX1BPUlQ9JEVNQUlMX1NNVFBfUE9SVAogICAgICAtIEVNQUlMX1NNVFBfVVNFUj0kRU1BSUxfU01UUF9VU0VSCiAgICAgIC0gRU1BSUxfU01UUF9QQVNTV09SRD0kRU1BSUxfU01UUF9QQVNTV09SRAogICAgICAtICdURUxFTUVUUllfRU5BQkxFRD0ke1RFTEVNRVRSWV9FTkFCTEVEOi1mYWxzZX0nCiAgICAgIC0gJ0NBQ0hFX1NUT1JBR0VfVFlQRT0ke0NBQ0hFX1NUT1JBR0VfVFlQRTotcmVkaXN9JwogICAgICAtIFJFRElTX0hPU1Q9cmVkaXMKICAgICAgLSBSRURJU19QT1JUPTYzNzkKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMC9oZWFsdGh6JwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3R3ZW50eWNybS90d2VudHktcG9zdGdyZXM6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfREI9ZGVmYXVsdAogICAgdm9sdW1lczoKICAgICAgLSAncGctZGF0YTovYml0bmFtaS9wb3N0Z3Jlc3FsJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHJlZGlzOgogICAgaW1hZ2U6ICdyZWRpczpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdyZWRpcy1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["crm","self-hosted","dashboard"],"logo":"svgs\/twenty.svg","minversion":"0.0.0","port":"3000"},"umami":{"documentation":"https:\/\/umami.is?utm_source=coolify.io","slogan":"Umami is web analytics platform which provides insights into visitor behavior without compromising user privacy.","compose":"c2VydmljZXM6CiAgdW1hbWk6CiAgICBpbWFnZTogJ2doY3IuaW8vdW1hbWktc29mdHdhcmUvdW1hbWk6cG9zdGdyZXNxbC1sYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVU1BTUlfMzAwMAogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzcWw6NTQzMi8kUE9TVEdSRVNfREInCiAgICAgIC0gREFUQUJBU0VfVFlQRT1wb3N0Z3JlcwogICAgICAtIEFQUF9TRUNSRVQ9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfVU1BTUkKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwL2FwaS9oZWFydGJlYXQnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotdW1hbWl9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["analytics","insights","privacy"],"logo":"svgs\/umami.svg","minversion":"0.0.0","port":"3000"},"unleash-with-postgresql":{"documentation":"https:\/\/docs.getunleash.io?utm_source=coolify.io","slogan":"Open source feature flag management for enterprises.","compose":"c2VydmljZXM6CiAgdW5sZWFzaDoKICAgIGltYWdlOiAndW5sZWFzaG9yZy91bmxlYXNoLXNlcnZlcjpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVU5MRUFTSF80MjQyCiAgICAgIC0gJ1VOTEVBU0hfVVJMPSR7U0VSVklDRV9GUUROX1VOTEVBU0h9JwogICAgICAtICdVTkxFQVNIX0RFRkFVTFRfQURNSU5fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1VOTEVBU0h9JwogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzL2RiJwogICAgICAtIERBVEFCQVNFX1NTTD1mYWxzZQogICAgICAtIExPR19MRVZFTD13YXJuCiAgICAgIC0gJ0lOSVRfRlJPTlRFTkRfQVBJX1RPS0VOUz1kZWZhdWx0OmRlZmF1bHQ6ZGV2ZWxvcG1lbnQudW5sZWFzaC1pbnNlY3VyZS1mcm9udGVuZC1hcGktdG9rZW4nCiAgICAgIC0gJ0lOSVRfQ0xJRU5UX0FQSV9UT0tFTlM9ZGVmYXVsdDpkZXZlbG9wbWVudC51bmxlYXNoLWluc2VjdXJlLWFwaS10b2tlbicKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBjb21tYW5kOgogICAgICAtIG5vZGUKICAgICAgLSBpbmRleC5qcwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6ICd3Z2V0IC0tbm8tdmVyYm9zZSAtLXRyaWVzPTEgLS1zcGlkZXIgaHR0cDovLzEyNy4wLjAuMTo0MjQyL2hlYWx0aCB8fCBleGl0IDEnCiAgICAgIGludGVydmFsOiAxcwogICAgICB0aW1lb3V0OiAxbQogICAgICByZXRyaWVzOiA1CiAgICAgIHN0YXJ0X3BlcmlvZDogMTVzCiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE1JwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfREI9ZGIKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBwZ19pc3JlYWR5CiAgICAgICAgLSAnLS11c2VybmFtZT0kU0VSVklDRV9VU0VSX1BPU1RHUkVTJwogICAgICAgIC0gJy0taG9zdD0xMjcuMC4wLjEnCiAgICAgICAgLSAnLS1wb3J0PTU0MzInCiAgICAgICAgLSAnLS1kYm5hbWU9ZGInCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxbQogICAgICByZXRyaWVzOiA1CiAgICAgIHN0YXJ0X3BlcmlvZDogMTBzCg==","tags":["unleash","feature flags","feature toggles","ab testing","open source"],"logo":"svgs\/unleash.svg","minversion":"0.0.0","port":"4242"},"unleash-without-database":{"documentation":"https:\/\/docs.getunleash.io?utm_source=coolify.io","slogan":"Open source feature flag management for enterprises.","compose":"c2VydmljZXM6CiAgdW5sZWFzaDoKICAgIGltYWdlOiAndW5sZWFzaG9yZy91bmxlYXNoLXNlcnZlcjpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVU5MRUFTSF80MjQyCiAgICAgIC0gJ1VOTEVBU0hfVVJMPSR7U0VSVklDRV9GUUROX1VOTEVBU0h9JwogICAgICAtICdVTkxFQVNIX0RFRkFVTFRfQURNSU5fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1VOTEVBU0h9JwogICAgICAtICdEQVRBQkFTRV9VUkw9JHtEQVRBQkFTRV9VUkx9JwogICAgICAtICdEQVRBQkFTRV9TU0w9JHtEQVRBQkFTRV9TU0w6LWZhbHNlfScKICAgICAgLSBMT0dfTEVWRUw9d2FybgogICAgICAtICdJTklUX0ZST05URU5EX0FQSV9UT0tFTlM9ZGVmYXVsdDpkZWZhdWx0OmRldmVsb3BtZW50LnVubGVhc2gtaW5zZWN1cmUtZnJvbnRlbmQtYXBpLXRva2VuJwogICAgICAtICdJTklUX0NMSUVOVF9BUElfVE9LRU5TPWRlZmF1bHQ6ZGV2ZWxvcG1lbnQudW5sZWFzaC1pbnNlY3VyZS1hcGktdG9rZW4nCiAgICBjb21tYW5kOgogICAgICAtIG5vZGUKICAgICAgLSBpbmRleC5qcwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6ICd3Z2V0IC0tbm8tdmVyYm9zZSAtLXRyaWVzPTEgLS1zcGlkZXIgaHR0cDovLzEyNy4wLjAuMTo0MjQyL2hlYWx0aCB8fCBleGl0IDEnCiAgICAgIGludGVydmFsOiAxcwogICAgICB0aW1lb3V0OiAxbQogICAgICByZXRyaWVzOiA1CiAgICAgIHN0YXJ0X3BlcmlvZDogMTVzCg==","tags":["unleash","feature flags","feature toggles","ab testing","open source"],"logo":"svgs\/unleash.svg","minversion":"0.0.0","port":"4242"},"uptime-kuma":{"documentation":"https:\/\/github.com\/louislam\/uptime-kuma?tab=readme-ov-file?utm_source=coolify.io","slogan":"Uptime Kuma is a monitoring tool for tracking the status and performance of your applications in real-time.","compose":"c2VydmljZXM6CiAgdXB0aW1lLWt1bWE6CiAgICBpbWFnZTogJ2xvdWlzbGFtL3VwdGltZS1rdW1hOjEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVVBUSU1FLUtVTUFfMzAwMQogICAgdm9sdW1lczoKICAgICAgLSAndXB0aW1lLWt1bWE6L2FwcC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtIGV4dHJhL2hlYWx0aGNoZWNrCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["monitoring","status","performance","web","services","applications","real-time"],"logo":"svgs\/uptime-kuma.svg","minversion":"0.0.0","port":"3001"},"vaultwarden":{"documentation":"https:\/\/github.com\/dani-garcia\/vaultwarden?utm_source=coolify.io","slogan":"Vaultwarden is a password manager that allows you to securely store and manage your passwords.","compose":"c2VydmljZXM6CiAgdmF1bHR3YXJkZW46CiAgICBpbWFnZTogJ3ZhdWx0d2FyZGVuL3NlcnZlcjpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVkFVTFRXQVJERU4KICAgICAgLSAnRE9NQUlOPSR7U0VSVklDRV9GUUROX1ZBVUxUV0FSREVOfScKICAgICAgLSAnREFUQUJBU0VfVVJMPSR7VkFVTFRXQVJERU5fREJfVVJMOi1kYXRhL2RiLnNxbGl0ZTN9JwogICAgICAtICdTSUdOVVBTX0FMTE9XRUQ9JHtTSUdOVVBfQUxMT1dFRDotdHJ1ZX0nCiAgICAgIC0gJ0FETUlOX1RPS0VOPSR7U0VSVklDRV9QQVNTV09SRF82NF9BRE1JTn0nCiAgICAgIC0gSVBfSEVBREVSPVgtRm9yd2FyZGVkLUZvcgogICAgICAtICdQVVNIX0VOQUJMRUQ9JHtQVVNIX0VOQUJMRUQ6LWZhbHNlfScKICAgICAgLSAnUFVTSF9JTlNUQUxMQVRJT05fSUQ9JHtQVVNIX1NFUlZJQ0VfSUR9JwogICAgICAtICdQVVNIX0lOU1RBTExBVElPTl9LRVk9JHtQVVNIX1NFUlZJQ0VfS0VZfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3ZhdWx0d2FyZGVuLWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["password manager","security"],"logo":"svgs\/bitwarden.svg","minversion":"0.0.0","port":"80"},"vikunja":{"documentation":"https:\/\/vikunja.io?utm_source=coolify.io","slogan":"The open-source, self-hostable to-do app. Organize everything, on all platforms.","compose":"c2VydmljZXM6CiAgdmlrdW5qYToKICAgIGltYWdlOiB2aWt1bmphL3Zpa3VuamEKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9WSUtVTkpBCiAgICAgIC0gVklLVU5KQV9TRVJWSUNFX1BVQkxJQ1VSTD0kU0VSVklDRV9GUUROX1ZJS1VOSkEKICAgICAgLSBWSUtVTkpBX1NFUlZJQ0VfSldUU0VDUkVUPSRTRVJWSUNFX1BBU1NXT1JEX0pXVFNFQ1JFVAogICAgICAtIFZJS1VOSkFfU0VSVklDRV9FTkFCTEVSRUdJU1RSQVRJT049dHJ1ZQogICAgdm9sdW1lczoKICAgICAgLSAndmlrdW5qYS1kYXRhOi9hcHAvdmlrdW5qYS8nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gd2dldAogICAgICAgIC0gJy0tc3BpZGVyJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzQ1NicKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["productivity","todo"],"logo":"svgs\/vikunja.svg","minversion":"0.0.0","port":"3456"},"weblate":{"documentation":"https:\/\/weblate.org?utm_source=coolify.io","slogan":"Weblate is a libre software web-based continuous localization system.","compose":"c2VydmljZXM6CiAgd2VibGF0ZToKICAgIGltYWdlOiAnd2VibGF0ZS93ZWJsYXRlOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9XRUJMQVRFXzgwODAKICAgICAgLSBXRUJMQVRFX1NJVEVfRE9NQUlOPSRTRVJWSUNFX1VSTF9XRUJMQVRFCiAgICAgIC0gJ1dFQkxBVEVfQURNSU5fTkFNRT0ke1dFQkxBVEVfQURNSU5fTkFNRTotQWRtaW59JwogICAgICAtICdXRUJMQVRFX0FETUlOX0VNQUlMPSR7V0VCTEFURV9BRE1JTl9FTUFJTDotYWRtaW5AZXhhbXBsZS5jb219JwogICAgICAtIFdFQkxBVEVfQURNSU5fUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfV0VCTEFURQogICAgICAtICdERUZBVUxUX0ZST01fRU1BSUw9JHtXRUJMQVRFX0FETUlOX0VNQUlMOi1hZG1pbkBleGFtcGxlLmNvbX0nCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREFUQUJBU0U9JHtQT1NUR1JFU19EQjotd2VibGF0ZX0nCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3Jlc3FsCiAgICAgIC0gUE9TVEdSRVNfUE9SVD01NDMyCiAgICAgIC0gUkVESVNfSE9TVD1yZWRpcwogICAgICAtIFJFRElTX1BPUlQ9NjM3OQogICAgICAtIFJFRElTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1JFRElTCiAgICB2b2x1bWVzOgogICAgICAtICd3ZWJsYXRlLWRhdGE6L2FwcC9kYXRhJwogICAgICAtICd3ZWJsYXRlLWNhY2hlOi9hcHAvY2FjaGUnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODA4MCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAzMAogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi13ZWJsYXRlfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6Ny1hbHBpbmUnCiAgICBjb21tYW5kOiAiLS1hcHBlbmRvbmx5IHllcyAtLXJlcXVpcmVwYXNzICR7U0VSVklDRV9QQVNTV09SRF9SRURJU31cbiIKICAgIGVudmlyb25tZW50OgogICAgICAtIFJFRElTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1JFRElTCiAgICB2b2x1bWVzOgogICAgICAtICd3ZWJsYXRlLXJlZGlzLWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["localization","translation","web","web-based","continuous","libre","software"],"logo":"svgs\/weblate.webp","minversion":"0.0.0","port":"8080"},"whoogle":{"documentation":"https:\/\/github.com\/benbusby\/whoogle-search?tab=readme-ov-file?utm_source=coolify.io","slogan":"Whoogle is a self-hosted, privacy-focused search engine front-end for accessing Google search results without tracking and data collection.","compose":"c2VydmljZXM6CiAgd2hvb2dsZToKICAgIGltYWdlOiAnYmVuYnVzYnkvd2hvb2dsZS1zZWFyY2g6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1dIT09HTEVfNTAwMAogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjUwMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["privacy","search engine"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"5000"},"wordpress-with-mariadb":{"documentation":"https:\/\/wordpress.org?utm_source=coolify.io","slogan":"Wordpress is open source software you can use to create a beautiful website, blog, or app.","compose":"c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fV09SRFBSRVNTCiAgICAgIC0gV09SRFBSRVNTX0RCX0hPU1Q9bWFyaWFkYgogICAgICAtIFdPUkRQUkVTU19EQl9VU0VSPSRTRVJWSUNFX1VTRVJfV09SRFBSRVNTCiAgICAgIC0gV09SRFBSRVNTX0RCX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1dPUkRQUkVTUwogICAgICAtIFdPUkRQUkVTU19EQl9OQU1FPXdvcmRwcmVzcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBtYXJpYWRiCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjEnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTAKICBtYXJpYWRiOgogICAgaW1hZ2U6ICdtYXJpYWRiOjExJwogICAgdm9sdW1lczoKICAgICAgLSAnbWFyaWFkYi1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTVlTUUxfUk9PVF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9ST09UCiAgICAgIC0gTVlTUUxfREFUQUJBU0U9d29yZHByZXNzCiAgICAgIC0gTVlTUUxfVVNFUj0kU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICAtIE1ZU1FMX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1dPUkRQUkVTUwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGhlYWx0aGNoZWNrLnNoCiAgICAgICAgLSAnLS1jb25uZWN0JwogICAgICAgIC0gJy0taW5ub2RiX2luaXRpYWxpemVkJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["cms","blog","content","management","mariadb"],"logo":"svgs\/wordpress.svg","minversion":"0.0.0"},"wordpress-with-mysql":{"documentation":"https:\/\/wordpress.org?utm_source=coolify.io","slogan":"Wordpress is open source software you can use to create a beautiful website, blog, or app.","compose":"c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fV09SRFBSRVNTCiAgICAgIC0gV09SRFBSRVNTX0RCX0hPU1Q9bXlzcWwKICAgICAgLSBXT1JEUFJFU1NfREJfVVNFUj0kU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICAtIFdPUkRQUkVTU19EQl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9XT1JEUFJFU1MKICAgICAgLSBXT1JEUFJFU1NfREJfTkFNRT13b3JkcHJlc3MKICAgIGRlcGVuZHNfb246CiAgICAgIC0gbXlzcWwKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMScKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxMAogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo1LjcnCiAgICB2b2x1bWVzOgogICAgICAtICdteXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTVlTUUxfUk9PVF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9ST09UCiAgICAgIC0gTVlTUUxfREFUQUJBU0U9d29yZHByZXNzCiAgICAgIC0gTVlTUUxfVVNFUj0kU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICAtIE1ZU1FMX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1dPUkRQUkVTUwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIG15c3FsYWRtaW4KICAgICAgICAtIHBpbmcKICAgICAgICAtICctaCcKICAgICAgICAtIDEyNy4wLjAuMQogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["cms","blog","content","management","mysql"],"logo":"svgs\/wordpress.svg","minversion":"0.0.0"},"wordpress-without-database":{"documentation":"https:\/\/wordpress.org?utm_source=coolify.io","slogan":"Wordpress is open source software you can use to create a beautiful website, blog, or app.","compose":"c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fV09SRFBSRVNTCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjEnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTAK","tags":["cms","blog","content","management"],"logo":"svgs\/wordpress.svg","minversion":"0.0.0"}} \ No newline at end of file +{"activepieces":{"documentation":"https:\/\/www.activepieces.com\/docs\/getting-started\/introduction?utm_source=coolify.io","slogan":"Open source no-code business automation.","compose":"c2VydmljZXM6CiAgYWN0aXZlcGllY2VzOgogICAgaW1hZ2U6ICdnaGNyLmlvL2FjdGl2ZXBpZWNlcy9hY3RpdmVwaWVjZXM6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gQVBfQVBJX0tFWT0kU0VSVklDRV9QQVNTV09SRF82NF9BUElLRVkKICAgICAgLSBBUF9FTkNSWVBUSU9OX0tFWT0kU0VSVklDRV9QQVNTV09SRF9FTkNSWVBUSU9OS0VZCiAgICAgIC0gQVBfRU5HSU5FX0VYRUNVVEFCTEVfUEFUSD1kaXN0L3BhY2thZ2VzL2VuZ2luZS9tYWluLmpzCiAgICAgIC0gQVBfRU5WSVJPTk1FTlQ9cHJvZAogICAgICAtIEFQX0VYRUNVVElPTl9NT0RFPVVOU0FOREJPWEVECiAgICAgIC0gQVBfRlJPTlRFTkRfVVJMPSRTRVJWSUNFX0ZRRE5fQUNUSVZFUElFQ0VTCiAgICAgIC0gQVBfSldUX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9KV1QKICAgICAgLSBBUF9QT1NUR1JFU19EQVRBQkFTRT1hY3RpdmVwaWVjZXMKICAgICAgLSBBUF9QT1NUR1JFU19IT1NUPXBvc3RncmVzCiAgICAgIC0gQVBfUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSBBUF9QT1NUR1JFU19QT1JUPTU0MzIKICAgICAgLSBBUF9QT1NUR1JFU19VU0VSTkFNRT0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gQVBfUkVESVNfSE9TVD1yZWRpcwogICAgICAtIEFQX1JFRElTX1BPUlQ9NjM3OQogICAgICAtIEFQX1NBTkRCT1hfUlVOX1RJTUVfU0VDT05EUz02MDAKICAgICAgLSBBUF9URUxFTUVUUllfRU5BQkxFRD10cnVlCiAgICAgIC0gJ0FQX1RFTVBMQVRFU19TT1VSQ0VfVVJMPWh0dHBzOi8vY2xvdWQuYWN0aXZlcGllY2VzLmNvbS9hcGkvdjEvZmxvdy10ZW1wbGF0ZXMnCiAgICAgIC0gQVBfVFJJR0dFUl9ERUZBVUxUX1BPTExfSU5URVJWQUw9NQogICAgICAtIEFQX1dFQkhPT0tfVElNRU9VVF9TRUNPTkRTPTMwCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3JlczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICByZWRpczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2Vfc3RhcnRlZAogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX0RCPWFjdGl2ZXBpZWNlcwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICB2b2x1bWVzOgogICAgICAtICdwZy1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAncmVkaXNfZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSByZWRpcy1jbGkKICAgICAgICAtIHBpbmcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["workflow","automation","no code","open source"],"logo":"svgs\/activepieces.png","minversion":"0.0.0"},"appsmith":{"documentation":"https:\/\/appsmith.com?utm_source=coolify.io","slogan":"A low-code application platform for building internal tools.","compose":"c2VydmljZXM6CiAgYXBwc21pdGg6CiAgICBpbWFnZTogJ2luZGV4LmRvY2tlci5pby9hcHBzbWl0aC9hcHBzbWl0aC1jZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fQVBQU01JVEgKICAgICAgLSBBUFBTTUlUSF9NQUlMX0VOQUJMRUQ9ZmFsc2UKICAgICAgLSBBUFBTTUlUSF9ESVNBQkxFX1RFTEVNRVRSWT10cnVlCiAgICAgIC0gQVBQU01JVEhfRElTQUJMRV9JTlRFUkNPTT10cnVlCiAgICAgIC0gQVBQU01JVEhfU0VOVFJZX0RTTj0KICAgICAgLSBBUFBTTUlUSF9TTUFSVF9MT09LX0lEPQogICAgdm9sdW1lczoKICAgICAgLSAnc3RhY2tzLWRhdGE6L2FwcHNtaXRoLXN0YWNrcycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["lowcode","nocode","no","low","platform"],"logo":"svgs\/appsmith.svg","minversion":"0.0.0"},"appwrite":{"documentation":"https:\/\/appwrite.io?utm_source=coolify.io","slogan":"A backend-as-a-service platform that simplifies the web & mobile app development.","compose":"eC1sb2dnaW5nOgogIGxvZ2dpbmc6CiAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgb3B0aW9uczoKICAgICAgbWF4LWZpbGU6ICc1JwogICAgICBtYXgtc2l6ZTogMTBtCnNlcnZpY2VzOgogIGFwcHdyaXRlOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLXVwbG9hZHM6L3N0b3JhZ2UvdXBsb2FkczpydycKICAgICAgLSAnYXBwd3JpdGUtY2FjaGU6L3N0b3JhZ2UvY2FjaGU6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNvbmZpZzovc3RvcmFnZS9jb25maWc6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNlcnRpZmljYXRlczovc3RvcmFnZS9jZXJ0aWZpY2F0ZXM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWZ1bmN0aW9uczovc3RvcmFnZS9mdW5jdGlvbnM6cncnCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0FQUFdSSVRFPS8KICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9MT0NBTEUKICAgICAgLSBfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX1JPT1QKICAgICAgLSBfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX0VNQUlMUwogICAgICAtIF9BUFBfQ09OU09MRV9XSElURUxJU1RfSVBTCiAgICAgIC0gX0FQUF9DT05TT0xFX0hPU1ROQU1FUwogICAgICAtIF9BUFBfU1lTVEVNX0VNQUlMX05BTUUKICAgICAgLSBfQVBQX1NZU1RFTV9FTUFJTF9BRERSRVNTCiAgICAgIC0gX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfU1lTVEVNX1JFU1BPTlNFX0ZPUk1BVAogICAgICAtIF9BUFBfT1BUSU9OU19BQlVTRQogICAgICAtIF9BUFBfT1BUSU9OU19GT1JDRV9IVFRQUwogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX0RPTUFJTj0kU0VSVklDRV9GUUROX0FQUFdSSVRFCiAgICAgIC0gX0FQUF9ET01BSU5fVEFSR0VUPSRTRVJWSUNFX0ZRRE5fQVBQV1JJVEUKICAgICAgLSBfQVBQX0RPTUFJTl9GVU5DVElPTlM9JFNFUlZJQ0VfRlFETl9BUFBXUklURQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9TTVRQX0hPU1QKICAgICAgLSBfQVBQX1NNVFBfUE9SVAogICAgICAtIF9BUFBfU01UUF9TRUNVUkUKICAgICAgLSBfQVBQX1NNVFBfVVNFUk5BTUUKICAgICAgLSBfQVBQX1NNVFBfUEFTU1dPUkQKICAgICAgLSBfQVBQX1VTQUdFX1NUQVRTCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTUlUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1BSRVZJRVdfTElNSVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQU5USVZJUlVTCiAgICAgIC0gX0FQUF9TVE9SQUdFX0FOVElWSVJVU19IT1NUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0FOVElWSVJVU19QT1JUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RFVklDRQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9TM19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQlVDS0VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfU0laRV9MSU1JVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19CVUlMRF9USU1FT1VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfQ1BVUwogICAgICAtIF9BUFBfRlVOQ1RJT05TX01FTU9SWQogICAgICAtIF9BUFBfRlVOQ1RJT05TX1JVTlRJTUVTCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9TRUNSRVQKICAgICAgLSBfQVBQX0VYRUNVVE9SX0hPU1QKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9JTlRFUlZBTAogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfREVMQVkKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9FWEVDVVRJT04KICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9DQUNIRQogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FCVVNFCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQVVESVQKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9VU0FHRV9IT1VSTFkKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9TQ0hFRFVMRVMKICAgICAgLSBfQVBQX1NNU19QUk9WSURFUgogICAgICAtIF9BUFBfU01TX0ZST00KICAgICAgLSBfQVBQX0dSQVBIUUxfTUFYX0JBVENIX1NJWkUKICAgICAgLSBfQVBQX0dSQVBIUUxfTUFYX0NPTVBMRVhJVFkKICAgICAgLSBfQVBQX0dSQVBIUUxfTUFYX0RFUFRICiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX0FQUF9OQU1FCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX1BSSVZBVEVfS0VZCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX0FQUF9JRAogICAgICAtIF9BUFBfVkNTX0dJVEhVQl9XRUJIT09LX1NFQ1JFVAogICAgICAtIF9BUFBfVkNTX0dJVEhVQl9DTElFTlRfU0VDUkVUCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX0NMSUVOVF9JRAogICAgICAtIF9BUFBfTUlHUkFUSU9OU19GSVJFQkFTRV9DTElFTlRfSUQKICAgICAgLSBfQVBQX01JR1JBVElPTlNfRklSRUJBU0VfQ0xJRU5UX1NFQ1JFVAogICAgICAtIF9BUFBfQVNTSVNUQU5UX09QRU5BSV9BUElfS0VZCiAgYXBwd3JpdGUtcmVhbHRpbWU6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHJlYWx0aW1lCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0FQUFdSSVRFPS92MS9yZWFsdGltZQogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QVElPTlNfQUJVU0UKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX1VTQUdFX1NUQVRTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1hdWRpdHM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1hdWRpdHMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItYXVkaXRzCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICAgIC0gYXBwd3JpdGUtbWFyaWFkYgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItd2ViaG9va3M6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci13ZWJob29rcwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci13ZWJob29rcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItZGVsZXRlczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41JwogICAgZW50cnlwb2ludDogd29ya2VyLWRlbGV0ZXMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItZGVsZXRlcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLXVwbG9hZHM6L3N0b3JhZ2UvdXBsb2FkczpydycKICAgICAgLSAnYXBwd3JpdGUtY2FjaGU6L3N0b3JhZ2UvY2FjaGU6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWZ1bmN0aW9uczovc3RvcmFnZS9mdW5jdGlvbnM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWJ1aWxkczovc3RvcmFnZS9idWlsZHM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNlcnRpZmljYXRlczovc3RvcmFnZS9jZXJ0aWZpY2F0ZXM6cncnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RFVklDRQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9TM19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQlVDS0VUCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfRVhFQ1VUT1JfU0VDUkVUCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9IT1NUCiAgYXBwd3JpdGUtd29ya2VyLWRhdGFiYXNlczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41JwogICAgZW50cnlwb2ludDogd29ya2VyLWRhdGFiYXNlcwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1kYXRhYmFzZXMKICAgIGRlcGVuZHNfb246CiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgICAgLSBhcHB3cml0ZS1tYXJpYWRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1idWlsZHM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1idWlsZHMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItYnVpbGRzCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICAgIC0gYXBwd3JpdGUtbWFyaWFkYgogICAgdm9sdW1lczoKICAgICAgLSAnYXBwd3JpdGUtZnVuY3Rpb25zOi9zdG9yYWdlL2Z1bmN0aW9uczpydycKICAgICAgLSAnYXBwd3JpdGUtYnVpbGRzOi9zdG9yYWdlL2J1aWxkczpydycKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9TRUNSRVQKICAgICAgLSBfQVBQX0VYRUNVVE9SX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX05BTUUKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfUFJJVkFURV9LRVkKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX0lECiAgICAgIC0gX0FQUF9GVU5DVElPTlNfVElNRU9VVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX0JVSUxEX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19DUFVTCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfTUVNT1JZCiAgICAgIC0gX0FQUF9PUFRJT05TX0ZPUkNFX0hUVFBTCiAgICAgIC0gX0FQUF9ET01BSU4KICAgICAgLSBfQVBQX1NUT1JBR0VfREVWSUNFCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9TM19CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9CVUNLRVQKICBhcHB3cml0ZS13b3JrZXItY2VydGlmaWNhdGVzOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItY2VydGlmaWNhdGVzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtd29ya2VyLWNlcnRpZmljYXRlcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLWNvbmZpZzovc3RvcmFnZS9jb25maWc6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNlcnRpZmljYXRlczovc3RvcmFnZS9jZXJ0aWZpY2F0ZXM6cncnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfRE9NQUlOCiAgICAgIC0gX0FQUF9ET01BSU5fVEFSR0VUCiAgICAgIC0gX0FQUF9ET01BSU5fRlVOQ1RJT05TCiAgICAgIC0gX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1mdW5jdGlvbnM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1mdW5jdGlvbnMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItZnVuY3Rpb25zCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICAgIC0gYXBwd3JpdGUtbWFyaWFkYgogICAgICAtIG9wZW5ydW50aW1lcy1leGVjdXRvcgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfRlVOQ1RJT05TX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19CVUlMRF9USU1FT1VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfQ1BVUwogICAgICAtIF9BUFBfRlVOQ1RJT05TX01FTU9SWQogICAgICAtIF9BUFBfRVhFQ1VUT1JfU0VDUkVUCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9IT1NUCiAgICAgIC0gX0FQUF9VU0FHRV9TVEFUUwogICAgICAtIF9BUFBfRE9DS0VSX0hVQl9VU0VSTkFNRQogICAgICAtIF9BUFBfRE9DS0VSX0hVQl9QQVNTV09SRAogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICBhcHB3cml0ZS13b3JrZXItbWFpbHM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1tYWlscwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1tYWlscwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1NZU1RFTV9FTUFJTF9OQU1FCiAgICAgIC0gX0FQUF9TWVNURU1fRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfU01UUF9IT1NUCiAgICAgIC0gX0FQUF9TTVRQX1BPUlQKICAgICAgLSBfQVBQX1NNVFBfU0VDVVJFCiAgICAgIC0gX0FQUF9TTVRQX1VTRVJOQU1FCiAgICAgIC0gX0FQUF9TTVRQX1BBU1NXT1JECiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1tZXNzYWdpbmc6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1tZXNzYWdpbmcKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItbWVzc2FnaW5nCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfU01TX0ZST00KICAgICAgLSBfQVBQX1NNU19QUk9WSURFUgogIGFwcHdyaXRlLXdvcmtlci1taWdyYXRpb25zOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItbWlncmF0aW9ucwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1taWdyYXRpb25zCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9ET01BSU4KICAgICAgLSBfQVBQX0RPTUFJTl9UQVJHRVQKICAgICAgLSBfQVBQX1NZU1RFTV9TRUNVUklUWV9FTUFJTF9BRERSRVNTCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgICAgIC0gX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9JRAogICAgICAtIF9BUFBfTUlHUkFUSU9OU19GSVJFQkFTRV9DTElFTlRfU0VDUkVUCiAgYXBwd3JpdGUtbWFpbnRlbmFuY2U6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IG1haW50ZW5hbmNlCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtbWFpbnRlbmFuY2UKICAgIGRlcGVuZHNfb246CiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX0RPTUFJTgogICAgICAtIF9BUFBfRE9NQUlOX1RBUkdFVAogICAgICAtIF9BUFBfRE9NQUlOX0ZVTkNUSU9OUwogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfSU5URVJWQUwKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9FWEVDVVRJT04KICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9DQUNIRQogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FCVVNFCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQVVESVQKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9VU0FHRV9IT1VSTFkKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9TQ0hFRFVMRVMKICBhcHB3cml0ZS13b3JrZXItdXNhZ2U6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNS4xJwogICAgZW50cnlwb2ludDogd29ya2VyLXVzYWdlCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtd29ya2VyLXVzYWdlCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX1VTQUdFX1NUQVRTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfVVNBR0VfQUdHUkVHQVRJT05fSU5URVJWQUwKICBhcHB3cml0ZS13b3JrZXItdXNhZ2UtZHVtcDoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41LjEnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItdXNhZ2UtZHVtcAogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci11c2FnZS1kdW1wCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICAgIC0gYXBwd3JpdGUtbWFyaWFkYgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfVVNBR0VfU1RBVFMKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgICAgIC0gX0FQUF9VU0FHRV9BR0dSRUdBVElPTl9JTlRFUlZBTAogIGFwcHdyaXRlLXNjaGVkdWxlci1mdW5jdGlvbnM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHNjaGVkdWxlLWZ1bmN0aW9ucwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXNjaGVkdWxlci1mdW5jdGlvbnMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogIGFwcHdyaXRlLXNjaGVkdWxlci1tZXNzYWdlczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41JwogICAgZW50cnlwb2ludDogc2NoZWR1bGUtbWVzc2FnZXMKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS1zY2hlZHVsZXItbWVzc2FnZXMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogIGFwcHdyaXRlLWFzc2lzdGFudDoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXNzaXN0YW50OjAuNC4wJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLWFzc2lzdGFudAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9BU1NJU1RBTlRfT1BFTkFJX0FQSV9LRVkKICBvcGVucnVudGltZXMtZXhlY3V0b3I6CiAgICBjb250YWluZXJfbmFtZTogb3BlbnJ1bnRpbWVzLWV4ZWN1dG9yCiAgICBob3N0bmFtZTogYXBwd3JpdGUtZXhlY3V0b3IKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHN0b3Bfc2lnbmFsOiBTSUdJTlQKICAgIGltYWdlOiAnb3BlbnJ1bnRpbWVzL2V4ZWN1dG9yOjAuNC45JwogICAgdm9sdW1lczoKICAgICAgLSAnL3Zhci9ydW4vZG9ja2VyLnNvY2s6L3Zhci9ydW4vZG9ja2VyLnNvY2snCiAgICAgIC0gJ2FwcHdyaXRlLWJ1aWxkczovc3RvcmFnZS9idWlsZHM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWZ1bmN0aW9uczovc3RvcmFnZS9mdW5jdGlvbnM6cncnCiAgICAgIC0gJy90bXA6L3RtcDpydycKICAgIGVudmlyb25tZW50OgogICAgICAtIE9QUl9FWEVDVVRPUl9JTkFDVElWRV9UUkVTSE9MRD0kX0FQUF9GVU5DVElPTlNfSU5BQ1RJVkVfVEhSRVNIT0xECiAgICAgIC0gT1BSX0VYRUNVVE9SX01BSU5URU5BTkNFX0lOVEVSVkFMPSRfQVBQX0ZVTkNUSU9OU19NQUlOVEVOQU5DRV9JTlRFUlZBTAogICAgICAtIE9QUl9FWEVDVVRPUl9ORVRXT1JLPSRfQVBQX0ZVTkNUSU9OU19SVU5USU1FU19ORVRXT1JLCiAgICAgIC0gT1BSX0VYRUNVVE9SX0RPQ0tFUl9IVUJfVVNFUk5BTUU9JF9BUFBfRE9DS0VSX0hVQl9VU0VSTkFNRQogICAgICAtIE9QUl9FWEVDVVRPUl9ET0NLRVJfSFVCX1BBU1NXT1JEPSRfQVBQX0RPQ0tFUl9IVUJfUEFTU1dPUkQKICAgICAgLSBPUFJfRVhFQ1VUT1JfRU5WPSRfQVBQX0VOVgogICAgICAtIE9QUl9FWEVDVVRPUl9SVU5USU1FUz0kX0FQUF9GVU5DVElPTlNfUlVOVElNRVMKICAgICAgLSBPUFJfRVhFQ1VUT1JfU0VDUkVUPSRfQVBQX0VYRUNVVE9SX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9MT0dHSU5HX1BST1ZJREVSPSRfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBPUFJfRVhFQ1VUT1JfTE9HR0lOR19DT05GSUc9JF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9ERVZJQ0U9JF9BUFBfU1RPUkFHRV9ERVZJQ0UKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9TM19BQ0NFU1NfS0VZPSRfQVBQX1NUT1JBR0VfUzNfQUNDRVNTX0tFWQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1MzX1NFQ1JFVD0kX0FQUF9TVE9SQUdFX1MzX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1MzX1JFR0lPTj0kX0FQUF9TVE9SQUdFX1MzX1JFR0lPTgogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1MzX0JVQ0tFVD0kX0FQUF9TVE9SQUdFX1MzX0JVQ0tFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0RPX1NQQUNFU19BQ0NFU1NfS0VZPSRfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0FDQ0VTU19LRVkKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9ET19TUEFDRVNfU0VDUkVUPSRfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT049JF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfUkVHSU9OCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVD0kX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19CVUNLRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9CQUNLQkxBWkVfQUNDRVNTX0tFWT0kX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfQkFDS0JMQVpFX1NFQ1JFVD0kX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9TRUNSRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9CQUNLQkxBWkVfUkVHSU9OPSRfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1JFR0lPTgogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0JBQ0tCTEFaRV9CVUNLRVQ9JF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQlVDS0VUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfTElOT0RFX0FDQ0VTU19LRVk9JF9BUFBfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0xJTk9ERV9TRUNSRVQ9JF9BUFBfU1RPUkFHRV9MSU5PREVfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfTElOT0RFX1JFR0lPTj0kX0FQUF9TVE9SQUdFX0xJTk9ERV9SRUdJT04KICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9MSU5PREVfQlVDS0VUPSRfQVBQX1NUT1JBR0VfTElOT0RFX0JVQ0tFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1dBU0FCSV9BQ0NFU1NfS0VZPSRfQVBQX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVkKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9XQVNBQklfU0VDUkVUPSRfQVBQX1NUT1JBR0VfV0FTQUJJX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1dBU0FCSV9SRUdJT049JF9BUFBfU1RPUkFHRV9XQVNBQklfUkVHSU9OCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfV0FTQUJJX0JVQ0tFVD0kX0FQUF9TVE9SQUdFX1dBU0FCSV9CVUNLRVQKICBhcHB3cml0ZS1tYXJpYWRiOgogICAgaW1hZ2U6ICdtYXJpYWRiOjEwLjExJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLW1hcmlhZGIKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLW1hcmlhZGI6L3Zhci9saWIvbXlzcWw6cncnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke19BUFBfREJfUk9PVF9QQVNTfScKICAgICAgLSAnTVlTUUxfREFUQUJBU0U9JHtfQVBQX0RCX1NDSEVNQX0nCiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtfQVBQX0RCX1VTRVJ9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke19BUFBfREJfUEFTU30nCiAgICBjb21tYW5kOiAnbXlzcWxkIC0taW5ub2RiLWZsdXNoLW1ldGhvZD1mc3luYycKICBhcHB3cml0ZS1yZWRpczoKICAgIGltYWdlOiAncmVkaXM6Ny4yLjQtYWxwaW5lJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXJlZGlzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb21tYW5kOiAicmVkaXMtc2VydmVyIC0tbWF4bWVtb3J5ICAgICAgICAgICAgNTEybWIgLS1tYXhtZW1vcnktcG9saWN5ICAgICBhbGxrZXlzLWxydSAtLW1heG1lbW9yeS1zYW1wbGVzICAgIDVcbiIKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLXJlZGlzOi9kYXRhOnJ3Jwp2b2x1bWVzOgogIGFwcHdyaXRlLW1hcmlhZGI6IG51bGwKICBhcHB3cml0ZS1yZWRpczogbnVsbAogIGFwcHdyaXRlLWNhY2hlOiBudWxsCiAgYXBwd3JpdGUtdXBsb2FkczogbnVsbAogIGFwcHdyaXRlLWNlcnRpZmljYXRlczogbnVsbAogIGFwcHdyaXRlLWZ1bmN0aW9uczogbnVsbAogIGFwcHdyaXRlLWJ1aWxkczogbnVsbAogIGFwcHdyaXRlLWNvbmZpZzogbnVsbAo=","tags":["backend-as-a-service","platform"],"logo":"svgs\/appwrite.svg","minversion":"0.0.0","envs":"X0FQUF9FTlY9cHJvZHVjdGlvbgpfQVBQX0xPQ0FMRT1lbgpfQVBQX09QVElPTlNfQUJVU0U9ZW5hYmxlZApfQVBQX09QVElPTlNfRk9SQ0VfSFRUUFM9ZGlzYWJsZWQKX0FQUF9PUEVOU1NMX0tFWV9WMT0KX0FQUF9ET01BSU49Cl9BUFBfRE9NQUlOX1RBUkdFVD0KX0FQUF9ET01BSU5fRlVOQ1RJT05TPQpfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX1JPT1Q9ZW5hYmxlZApfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX0VNQUlMUz0KX0FQUF9DT05TT0xFX1dISVRFTElTVF9JUFM9Cl9BUFBfQ09OU09MRV9IT1NUTkFNRVM9bG9jYWxob3N0LGFwcHdyaXRlLmlvLCouYXBwd3JpdGUuaW8KX0FQUF9TWVNURU1fRU1BSUxfTkFNRT1BcHB3cml0ZQpfQVBQX1NZU1RFTV9FTUFJTF9BRERSRVNTPXRlYW1AYXBwd3JpdGUuaW8KX0FQUF9TWVNURU1fUkVTUE9OU0VfRk9STUFUPQpfQVBQX1NZU1RFTV9TRUNVUklUWV9FTUFJTF9BRERSRVNTPWNlcnRzQGFwcHdyaXRlLmlvCl9BUFBfVVNBR0VfU1RBVFM9ZW5hYmxlZApfQVBQX0xPR0dJTkdfUFJPVklERVI9Cl9BUFBfTE9HR0lOR19DT05GSUc9Cl9BUFBfVVNBR0VfQUdHUkVHQVRJT05fSU5URVJWQUw9MzAKX0FQUF9VU0FHRV9USU1FU0VSSUVTX0lOVEVSVkFMPTMwCl9BUFBfVVNBR0VfREFUQUJBU0VfSU5URVJWQUw9OTAwCl9BUFBfV09SS0VSX1BFUl9DT1JFPTYKX0FQUF9SRURJU19IT1NUPWFwcHdyaXRlLXJlZGlzCl9BUFBfUkVESVNfUE9SVD02Mzc5Cl9BUFBfUkVESVNfVVNFUj0KX0FQUF9SRURJU19QQVNTPQpfQVBQX0RCX0hPU1Q9YXBwd3JpdGUtbWFyaWFkYgpfQVBQX0RCX1BPUlQ9MzMwNgpfQVBQX0RCX1NDSEVNQT1hcHB3cml0ZQpfQVBQX0RCX1VTRVI9JFNFUlZJQ0VfVVNFUl9NWVNRTApfQVBQX0RCX1BBU1M9JFNFUlZJQ0VfUEFTU1dPUkRfTVlTUUwKX0FQUF9EQl9ST09UX1BBU1M9JFNFUlZJQ0VfUEFTU1dPUkRfUk9PVE1ZU1FMCl9BUFBfU01UUF9IT1NUPQpfQVBQX1NNVFBfUE9SVD0KX0FQUF9TTVRQX1NFQ1VSRT0KX0FQUF9TTVRQX1VTRVJOQU1FPQpfQVBQX1NNVFBfUEFTU1dPUkQ9Cl9BUFBfU01TX1BST1ZJREVSPQpfQVBQX1NNU19GUk9NPQpfQVBQX1NUT1JBR0VfTElNSVQ9MzAwMDAwMDAKX0FQUF9TVE9SQUdFX1BSRVZJRVdfTElNSVQ9MjAwMDAwMDAKX0FQUF9TVE9SQUdFX0FOVElWSVJVUz1kaXNhYmxlZApfQVBQX1NUT1JBR0VfQU5USVZJUlVTX0hPU1Q9YXBwd3JpdGUtY2xhbWF2Cl9BUFBfU1RPUkFHRV9BTlRJVklSVVNfUE9SVD0zMzEwCl9BUFBfU1RPUkFHRV9ERVZJQ0U9bG9jYWwKX0FQUF9TVE9SQUdFX1MzX0FDQ0VTU19LRVk9Cl9BUFBfU1RPUkFHRV9TM19TRUNSRVQ9Cl9BUFBfU1RPUkFHRV9TM19SRUdJT049dXMtZWFzdC0xCl9BUFBfU1RPUkFHRV9TM19CVUNLRVQ9Cl9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQUNDRVNTX0tFWT0KX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19TRUNSRVQ9Cl9BUFBfU1RPUkFHRV9ET19TUEFDRVNfUkVHSU9OPXVzLWVhc3QtMQpfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVD0KX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZPQpfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1NFQ1JFVD0KX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9SRUdJT049dXMtd2VzdC0wMDQKX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9CVUNLRVQ9Cl9BUFBfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWT0KX0FQUF9TVE9SQUdFX0xJTk9ERV9TRUNSRVQ9Cl9BUFBfU1RPUkFHRV9MSU5PREVfUkVHSU9OPWV1LWNlbnRyYWwtMQpfQVBQX1NUT1JBR0VfTElOT0RFX0JVQ0tFVD0KX0FQUF9TVE9SQUdFX1dBU0FCSV9BQ0NFU1NfS0VZPQpfQVBQX1NUT1JBR0VfV0FTQUJJX1NFQ1JFVD0KX0FQUF9TVE9SQUdFX1dBU0FCSV9SRUdJT049ZXUtY2VudHJhbC0xCl9BUFBfU1RPUkFHRV9XQVNBQklfQlVDS0VUPQpfQVBQX0ZVTkNUSU9OU19TSVpFX0xJTUlUPTMwMDAwMDAwCl9BUFBfRlVOQ1RJT05TX1RJTUVPVVQ9OTAwCl9BUFBfRlVOQ1RJT05TX0JVSUxEX1RJTUVPVVQ9OTAwCl9BUFBfRlVOQ1RJT05TX0NPTlRBSU5FUlM9MTAKX0FQUF9GVU5DVElPTlNfQ1BVUz0wCl9BUFBfRlVOQ1RJT05TX01FTU9SWT0wCl9BUFBfRlVOQ1RJT05TX01FTU9SWV9TV0FQPTAKX0FQUF9GVU5DVElPTlNfUlVOVElNRVM9bm9kZS0yMC4wLHBocC04LjIscHl0aG9uLTMuMTEscnVieS0zLjIKX0FQUF9FWEVDVVRPUl9TRUNSRVQ9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfQVBQV1JJVEUKX0FQUF9FWEVDVVRPUl9IT1NUPWh0dHA6Ly9hcHB3cml0ZS1leGVjdXRvci92MQpfQVBQX0VYRUNVVE9SX1JVTlRJTUVfTkVUV09SSz1hcHB3cml0ZV9ydW50aW1lcwpfQVBQX0ZVTkNUSU9OU19JTkFDVElWRV9USFJFU0hPTEQ9NjAKRE9DS0VSSFVCX1BVTExfVVNFUk5BTUU9CkRPQ0tFUkhVQl9QVUxMX1BBU1NXT1JEPQpET0NLRVJIVUJfUFVMTF9FTUFJTD0KT1BFTl9SVU5USU1FU19ORVRXT1JLPWFwcHdyaXRlX3J1bnRpbWVzCl9BUFBfRlVOQ1RJT05TX1JVTlRJTUVTX05FVFdPUks9cnVudGltZXMKX0FQUF9ET0NLRVJfSFVCX1VTRVJOQU1FPQpfQVBQX0RPQ0tFUl9IVUJfUEFTU1dPUkQ9Cl9BUFBfRlVOQ1RJT05TX01BSU5URU5BTkNFX0lOVEVSVkFMPTM2MDAKX0FQUF9WQ1NfR0lUSFVCX0FQUF9OQU1FPQpfQVBQX1ZDU19HSVRIVUJfUFJJVkFURV9LRVk9Cl9BUFBfVkNTX0dJVEhVQl9BUFBfSUQ9Cl9BUFBfVkNTX0dJVEhVQl9DTElFTlRfSUQ9Cl9BUFBfVkNTX0dJVEhVQl9DTElFTlRfU0VDUkVUPQpfQVBQX1ZDU19HSVRIVUJfV0VCSE9PS19TRUNSRVQ9Cl9BUFBfTUFJTlRFTkFOQ0VfREVMQVk9Cl9BUFBfTUFJTlRFTkFOQ0VfSU5URVJWQUw9ODY0MDAKX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQ0FDSEU9MjU5MjAwMApfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9FWEVDVVRJT049MTIwOTYwMApfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9BVURJVD0xMjA5NjAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FCVVNFPTg2NDAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1VTQUdFX0hPVVJMWT04NjQwMDAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1NDSEVEVUxFUz04NjQwMApfQVBQX0dSQVBIUUxfTUFYX0JBVENIX1NJWkU9MTAKX0FQUF9HUkFQSFFMX01BWF9DT01QTEVYSVRZPTI1MApfQVBQX0dSQVBIUUxfTUFYX0RFUFRIPTMKX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9JRD0KX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9TRUNSRVQ9Cl9BUFBfQVNTSVNUQU5UX09QRU5BSV9BUElfS0VZPQo="},"authentik":{"documentation":"https:\/\/docs.goauthentik.io\/docs\/installation\/docker-compose?utm_source=coolify.io","slogan":"An open-source Identity Provider, focused on flexibility and versatility.","compose":"c2VydmljZXM6CiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAnZG9ja2VyLmlvL2xpYnJhcnkvcG9zdGdyZXM6MTItYWxwaW5lJwogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtZCAkJHtQT1NUR1JFU19EQn0gLVUgJCR7UE9TVEdSRVNfVVNFUn0nCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2F1dGhlbnRpay1kYjovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtIFBPU1RHUkVTX0RCPWF1dGhlbnRpawogIHJlZGlzOgogICAgaW1hZ2U6ICdkb2NrZXIuaW8vbGlicmFyeS9yZWRpczphbHBpbmUnCiAgICBjb21tYW5kOiAnLS1zYXZlIDYwIDEgLS1sb2dsZXZlbCB3YXJuaW5nJwogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncmVkaXMtY2xpIHBpbmcgfCBncmVwIFBPTkcnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3JlZGlzOi9kYXRhJwogIGF1dGhlbnRpay1zZXJ2ZXI6CiAgICBpbWFnZTogJ2doY3IuaW8vZ29hdXRoZW50aWsvc2VydmVyOiR7QVVUSEVOVElLX1RBRzotMjAyNC4yLjJ9JwogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGNvbW1hbmQ6IHNlcnZlcgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0FVVEhFTlRJS1NFUlZFUl85MDAwCiAgICAgIC0gQVVUSEVOVElLX1JFRElTX19IT1NUPXJlZGlzCiAgICAgIC0gQVVUSEVOVElLX1BPU1RHUkVTUUxfX0hPU1Q9cG9zdGdyZXNxbAogICAgICAtICdBVVRIRU5USUtfUE9TVEdSRVNRTF9fVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMfScKICAgICAgLSBBVVRIRU5USUtfUE9TVEdSRVNRTF9fTkFNRT1hdXRoZW50aWsKICAgICAgLSAnQVVUSEVOVElLX1BPU1RHUkVTUUxfX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMfScKICAgICAgLSAnQVVUSEVOVElLX1NFQ1JFVF9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEXzY0X0FVVEhFTlRJS1NFUlZFUn0nCiAgICAgIC0gJ0FVVEhFTlRJS19FUlJPUl9SRVBPUlRJTkdfX0VOQUJMRUQ9JHtBVVRIRU5USUtfRVJST1JfUkVQT1JUSU5HX19FTkFCTEVEOi10cnVlfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19IT1NUPSR7QVVUSEVOVElLX0VNQUlMX19IT1NUfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19QT1JUPSR7QVVUSEVOVElLX0VNQUlMX19QT1JUfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19VU0VSTkFNRT0ke0FVVEhFTlRJS19FTUFJTF9fVVNFUk5BTUV9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1BBU1NXT1JEPSR7QVVUSEVOVElLX0VNQUlMX19QQVNTV09SRH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fVVNFX1RMUz0ke0FVVEhFTlRJS19FTUFJTF9fVVNFX1RMU30nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fVVNFX1NTTD0ke0FVVEhFTlRJS19FTUFJTF9fVVNFX1NTTH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fVElNRU9VVD0ke0FVVEhFTlRJS19FTUFJTF9fVElNRU9VVH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fRlJPTT0ke0FVVEhFTlRJS19FTUFJTF9fRlJPTX0nCiAgICB2b2x1bWVzOgogICAgICAtICcuL21lZGlhOi9tZWRpYScKICAgICAgLSAnLi9jdXN0b20tdGVtcGxhdGVzOi90ZW1wbGF0ZXMnCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3Jlc3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHJlZGlzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgYXV0aGVudGlrLXdvcmtlcjoKICAgIGltYWdlOiAnZ2hjci5pby9nb2F1dGhlbnRpay9zZXJ2ZXI6JHtBVVRIRU5USUtfVEFHOi0yMDI0LjIuMn0nCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgY29tbWFuZDogd29ya2VyCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBBVVRIRU5USUtfUkVESVNfX0hPU1Q9cmVkaXMKICAgICAgLSBBVVRIRU5USUtfUE9TVEdSRVNRTF9fSE9TVD1wb3N0Z3Jlc3FsCiAgICAgIC0gJ0FVVEhFTlRJS19QT1NUR1JFU1FMX19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtIEFVVEhFTlRJS19QT1NUR1JFU1FMX19OQU1FPWF1dGhlbnRpawogICAgICAtICdBVVRIRU5USUtfUE9TVEdSRVNRTF9fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdBVVRIRU5USUtfU0VDUkVUX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfQVVUSEVOVElLU0VSVkVSfScKICAgICAgLSAnQVVUSEVOVElLX0VSUk9SX1JFUE9SVElOR19fRU5BQkxFRD0ke0FVVEhFTlRJS19FUlJPUl9SRVBPUlRJTkdfX0VOQUJMRUR9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX0hPU1Q9JHtBVVRIRU5USUtfRU1BSUxfX0hPU1R9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1BPUlQ9JHtBVVRIRU5USUtfRU1BSUxfX1BPUlR9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1VTRVJOQU1FPSR7QVVUSEVOVElLX0VNQUlMX19VU0VSTkFNRX0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fUEFTU1dPUkQ9JHtBVVRIRU5USUtfRU1BSUxfX1BBU1NXT1JEfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19VU0VfVExTPSR7QVVUSEVOVElLX0VNQUlMX19VU0VfVExTfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19VU0VfU1NMPSR7QVVUSEVOVElLX0VNQUlMX19VU0VfU1NMfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19USU1FT1VUPSR7QVVUSEVOVElLX0VNQUlMX19USU1FT1VUfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19GUk9NPSR7QVVUSEVOVElLX0VNQUlMX19GUk9NfScKICAgIHVzZXI6IHJvb3QKICAgIHZvbHVtZXM6CiAgICAgIC0gJy92YXIvcnVuL2RvY2tlci5zb2NrOi92YXIvcnVuL2RvY2tlci5zb2NrJwogICAgICAtICcuL21lZGlhOi9tZWRpYScKICAgICAgLSAnLi9jZXJ0czovY2VydHMnCiAgICAgIC0gJy4vY3VzdG9tLXRlbXBsYXRlczovdGVtcGxhdGVzJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICByZWRpczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQo=","tags":["identity","login","user","oauth","openid","oidc","authentication","saml","auth0","okta"],"logo":"svgs\/authentik.png","minversion":"0.0.0","port":"9000"},"babybuddy":{"documentation":"https:\/\/docs.baby-buddy.net?utm_source=coolify.io","slogan":"It helps parents track their baby's daily activities, growth, and health with ease.","compose":"c2VydmljZXM6CiAgYmFieWJ1ZGR5OgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL2JhYnlidWRkeTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fQkFCWUJVRERZCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgICAtIENTUkZfVFJVU1RFRF9PUklHSU5TPSRTRVJWSUNFX0ZRRE5fQkFCWUJVRERZCiAgICB2b2x1bWVzOgogICAgICAtICdiYWJ5YnVkZHktY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAwMCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=","tags":["baby","parents","health","growth","activities"],"logo":"svgs\/babybuddy.png","minversion":"0.0.0"},"budge":{"documentation":"https:\/\/github.com\/linuxserver\/budge?utm_source=coolify.io","slogan":"A budgeting personal finance app.","compose":"c2VydmljZXM6CiAgYnVkZ2U6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvYnVkZ2U6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0JVREdFCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnYnVkZ2UtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["personal finance","budgeting","expense tracking"],"logo":"svgs\/unknown.svg","minversion":"0.0.0"},"changedetection":{"documentation":"https:\/\/github.com\/dgtlmoon\/changedetection.io\/?utm_source=coolify.io","slogan":"Website change detection monitor and notifications.","compose":"c2VydmljZXM6CiAgY2hhbmdlZGV0ZWN0aW9uOgogICAgaW1hZ2U6IGdoY3IuaW8vZGd0bG1vb24vY2hhbmdlZGV0ZWN0aW9uLmlvCiAgICB2b2x1bWVzOgogICAgICAtICdjaGFuZ2VkZXRlY3Rpb24tZGF0YTovZGF0YXN0b3JlJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NIQU5HRURFVEVDVElPTl81MDAwCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gQkFTRV9VUkw9JFNFUlZJQ0VfRlFETl9DSEFOR0VERVRFQ1RJT04KICAgICAgLSAnUExBWVdSSUdIVF9EUklWRVJfVVJMPXdzOi8vcGxheXdyaWdodC1jaHJvbWU6MzAwMC8\/c3RlYWx0aD0xJi0tZGlzYWJsZS13ZWItc2VjdXJpdHk9dHJ1ZScKICAgICAgLSBISURFX1JFRkVSRVI9dHJ1ZQogICAgZGVwZW5kc19vbjoKICAgICAgcGxheXdyaWdodC1jaHJvbWU6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBlY2hvCiAgICAgICAgLSBvawogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcGxheXdyaWdodC1jaHJvbWU6CiAgICBpbWFnZTogJ2RndGxtb29uL3NvY2twdXBwZXRicm93c2VyOmxhdGVzdCcKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTQ1JFRU5fV0lEVEg9MTkyMAogICAgICAtIFNDUkVFTl9IRUlHSFQ9MTAyNAogICAgICAtIFNDUkVFTl9ERVBUSD0xNgogICAgICAtIE1BWF9DT05DVVJSRU5UX0NIUk9NRV9QUk9DRVNTRVM9MTAKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBlY2hvCiAgICAgICAgLSBvawogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["web","alert","monitor"],"logo":"svgs\/changedetection.png","minversion":"0.0.0","port":"5000"},"chatwoot":{"documentation":"https:\/\/www.chatwoot.com\/docs\/self-hosted\/?utm_source=coolify.io","slogan":"Delightful customer relationships at scale.","compose":"c2VydmljZXM6CiAgcmFpbHM6CiAgICBpbWFnZTogJ2NoYXR3b290L2NoYXR3b290OmxhdGVzdCcKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcG9zdGdyZXMKICAgICAgLSByZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NIQVRXT09UXzMwMDAKICAgICAgLSBTRUNSRVRfS0VZX0JBU0U9JFNFUlZJQ0VfUEFTU1dPUkRfQ0hBVFdPT1QKICAgICAgLSAnRlJPTlRFTkRfVVJMPSR7U0VSVklDRV9GUUROX0NIQVRXT09UfScKICAgICAgLSAnREVGQVVMVF9MT0NBTEU9JHtDSEFUV09PVF9ERUZBVUxUX0xPQ0FMRX0nCiAgICAgIC0gRk9SQ0VfU1NMPWZhbHNlCiAgICAgIC0gRU5BQkxFX0FDQ09VTlRfU0lHTlVQPWZhbHNlCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovL2RlZmF1bHRAcmVkaXM6NjM3OScKICAgICAgLSBSRURJU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9SRURJUwogICAgICAtIFJFRElTX09QRU5TU0xfVkVSSUZZX01PREU9bm9uZQogICAgICAtIFBPU1RHUkVTX0RBVEFCQVNFPWNoYXR3b290CiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1VTRVJOQU1FPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVNfVVNFUgogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gUkFJTFNfTUFYX1RIUkVBRFM9NQogICAgICAtIE5PREVfRU5WPXByb2R1Y3Rpb24KICAgICAgLSBSQUlMU19FTlY9cHJvZHVjdGlvbgogICAgICAtIElOU1RBTExBVElPTl9FTlY9ZG9ja2VyCiAgICAgIC0gJ01BSUxFUl9TRU5ERVJfRU1BSUw9JHtDSEFUV09PVF9NQUlMRVJfU0VOREVSX0VNQUlMfScKICAgICAgLSAnU01UUF9BRERSRVNTPSR7Q0hBVFdPT1RfU01UUF9BRERSRVNTfScKICAgICAgLSAnU01UUF9BVVRIRU5USUNBVElPTj0ke0NIQVRXT09UX1NNVFBfQVVUSEVOVElDQVRJT059JwogICAgICAtICdTTVRQX0RPTUFJTj0ke0NIQVRXT09UX1NNVFBfRE9NQUlOfScKICAgICAgLSAnU01UUF9FTkFCTEVfU1RBUlRUTFNfQVVUTz0ke0NIQVRXT09UX1NNVFBfRU5BQkxFX1NUQVJUVExTX0FVVE99JwogICAgICAtICdTTVRQX1BPUlQ9JHtDSEFUV09PVF9TTVRQX1BPUlR9JwogICAgICAtICdTTVRQX1VTRVJOQU1FPSR7Q0hBVFdPT1RfU01UUF9VU0VSTkFNRX0nCiAgICAgIC0gJ1NNVFBfUEFTU1dPUkQ9JHtDSEFUV09PVF9TTVRQX1BBU1NXT1JEfScKICAgICAgLSBBQ1RJVkVfU1RPUkFHRV9TRVJWSUNFPWxvY2FsCiAgICBlbnRyeXBvaW50OiBkb2NrZXIvZW50cnlwb2ludHMvcmFpbHMuc2gKICAgIGNvbW1hbmQ6ICdzaCAtYyAiYnVuZGxlIGV4ZWMgcmFpbHMgZGI6Y2hhdHdvb3RfcHJlcGFyZSAmJiBidW5kbGUgZXhlYyByYWlscyBzIC1wIDMwMDAgLWIgMC4wLjAuMCInCiAgICB2b2x1bWVzOgogICAgICAtICdyYWlscy1kYXRhOi9hcHAvc3RvcmFnZScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnLXEnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgc2lkZWtpcToKICAgIGltYWdlOiAnY2hhdHdvb3QvY2hhdHdvb3Q6bGF0ZXN0JwogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3JlcwogICAgICAtIHJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRUNSRVRfS0VZX0JBU0U9JFNFUlZJQ0VfUEFTU1dPUkRfQ0hBVFdPT1QKICAgICAgLSAnRlJPTlRFTkRfVVJMPSR7U0VSVklDRV9GUUROX0NIQVRXT09UfScKICAgICAgLSAnREVGQVVMVF9MT0NBTEU9JHtDSEFUV09PVF9ERUZBVUxUX0xPQ0FMRX0nCiAgICAgIC0gRk9SQ0VfU1NMPWZhbHNlCiAgICAgIC0gRU5BQkxFX0FDQ09VTlRfU0lHTlVQPWZhbHNlCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovL2RlZmF1bHRAcmVkaXM6NjM3OScKICAgICAgLSBSRURJU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9SRURJUwogICAgICAtIFJFRElTX09QRU5TU0xfVkVSSUZZX01PREU9bm9uZQogICAgICAtIFBPU1RHUkVTX0RBVEFCQVNFPWNoYXR3b290CiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1VTRVJOQU1FPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVNfVVNFUgogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gUkFJTFNfTUFYX1RIUkVBRFM9NQogICAgICAtIE5PREVfRU5WPXByb2R1Y3Rpb24KICAgICAgLSBSQUlMU19FTlY9cHJvZHVjdGlvbgogICAgICAtIElOU1RBTExBVElPTl9FTlY9ZG9ja2VyCiAgICAgIC0gJ01BSUxFUl9TRU5ERVJfRU1BSUw9JHtDSEFUV09PVF9NQUlMRVJfU0VOREVSX0VNQUlMfScKICAgICAgLSAnU01UUF9BRERSRVNTPSR7Q0hBVFdPT1RfU01UUF9BRERSRVNTfScKICAgICAgLSAnU01UUF9BVVRIRU5USUNBVElPTj0ke0NIQVRXT09UX1NNVFBfQVVUSEVOVElDQVRJT059JwogICAgICAtICdTTVRQX0RPTUFJTj0ke0NIQVRXT09UX1NNVFBfRE9NQUlOfScKICAgICAgLSAnU01UUF9FTkFCTEVfU1RBUlRUTFNfQVVUTz0ke0NIQVRXT09UX1NNVFBfRU5BQkxFX1NUQVJUVExTX0FVVE99JwogICAgICAtICdTTVRQX1BPUlQ9JHtDSEFUV09PVF9TTVRQX1BPUlR9JwogICAgICAtICdTTVRQX1VTRVJOQU1FPSR7Q0hBVFdPT1RfU01UUF9VU0VSTkFNRX0nCiAgICAgIC0gJ1NNVFBfUEFTU1dPUkQ9JHtDSEFUV09PVF9TTVRQX1BBU1NXT1JEfScKICAgICAgLSBBQ1RJVkVfU1RPUkFHRV9TRVJWSUNFPWxvY2FsCiAgICBjb21tYW5kOgogICAgICAtIGJ1bmRsZQogICAgICAtIGV4ZWMKICAgICAgLSBzaWRla2lxCiAgICAgIC0gJy1DJwogICAgICAtIGNvbmZpZy9zaWRla2lxLnltbAogICAgdm9sdW1lczoKICAgICAgLSAnc2lkZWtpcS1kYXRhOi9hcHAvc3RvcmFnZScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAiYnVuZGxlIGV4ZWMgcmFpbHMgcnVubmVyICdwdXRzIFNpZGVraXEucmVkaXMoJjppbmZvKScgPiAvZGV2L251bGwgMj4mMSIKICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwogIHBvc3RncmVzOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxMicKICAgIHJlc3RhcnQ6IGFsd2F5cwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXMtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19EQj1jaGF0d29vdAogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFU19VU0VSCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkU0VSVklDRV9VU0VSX1BPU1RHUkVTX1VTRVIgLWQgY2hhdHdvb3QgLWggMTI3LjAuMC4xJwogICAgICBpbnRlcnZhbDogMzBzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiA1CiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOmFscGluZScKICAgIHJlc3RhcnQ6IGFsd2F5cwogICAgY29tbWFuZDoKICAgICAgLSBzaAogICAgICAtICctYycKICAgICAgLSAncmVkaXMtc2VydmVyIC0tcmVxdWlyZXBhc3MgIiRTRVJWSUNFX1BBU1NXT1JEX1JFRElTIicKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3JlZGlzLWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSAnLWEnCiAgICAgICAgLSAkU0VSVklDRV9QQVNTV09SRF9SRURJUwogICAgICAgIC0gUElORwogICAgICBpbnRlcnZhbDogMzBzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiA1Cg==","tags":["chatwoot","chat","api","open","source","rails","redis","postgresql","sidekiq"],"logo":"svgs\/chatwoot.svg","minversion":"0.0.0","port":"3000"},"classicpress-with-mariadb":{"documentation":"https:\/\/www.classicpress.net\/?utm_source=coolify.io","slogan":"A lightweight, stable, instantly familiar free open-source content management system, based on WordPress without the block editor (Gutenberg).","compose":"c2VydmljZXM6CiAgY2xhc3NpY3ByZXNzOgogICAgaW1hZ2U6ICdjbGFzc2ljcHJlc3MvY2xhc3NpY3ByZXNzOmxhdGVzdCcKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2NsYXNzaWNwcmVzcy1maWxlczovdmFyL3d3dy9odG1sJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NMQVNTSUNQUkVTUwogICAgICAtIENMQVNTSUNQUkVTU19EQl9IT1NUPW1hcmlhZGIKICAgICAgLSBDTEFTU0lDUFJFU1NfREJfVVNFUj0kU0VSVklDRV9VU0VSX0NMQVNTSUNQUkVTUwogICAgICAtIENMQVNTSUNQUkVTU19EQl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9DTEFTU0lDUFJFU1MKICAgICAgLSBDTEFTU0lDUFJFU1NfREJfTkFNRT1jbGFzc2ljcHJlc3MKICAgIGRlcGVuZHNfb246CiAgICAgIC0gbWFyaWFkYgogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDEwCiAgbWFyaWFkYjoKICAgIGltYWdlOiAnbWFyaWFkYjoxMScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ21hcmlhZGItZGF0YTovdmFyL2xpYi9teXNxbCcKICAgIGVudmlyb25tZW50OgogICAgICAtIE1ZU1FMX1JPT1RfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUk9PVAogICAgICAtIE1ZU1FMX0RBVEFCQVNFPWNsYXNzaWNwcmVzcwogICAgICAtIE1ZU1FMX1VTRVI9JFNFUlZJQ0VfVVNFUl9DTEFTU0lDUFJFU1MKICAgICAgLSBNWVNRTF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9DTEFTU0lDUFJFU1MKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBoZWFsdGhjaGVjay5zaAogICAgICAgIC0gJy0tY29ubmVjdCcKICAgICAgICAtICctLWlubm9kYl9pbml0aWFsaXplZCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["cms","blog","content","management"],"logo":"svgs\/classicpress.svg","minversion":"0.0.0"},"classicpress-with-mysql":{"documentation":"https:\/\/www.classicpress.net\/?utm_source=coolify.io","slogan":"A lightweight, stable, instantly familiar free open-source content management system, based on WordPress without the block editor (Gutenberg).","compose":"c2VydmljZXM6CiAgY2xhc3NpY3ByZXNzOgogICAgaW1hZ2U6ICdjbGFzc2ljcHJlc3MvY2xhc3NpY3ByZXNzOmxhdGVzdCcKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2NsYXNzaWNwcmVzcy1maWxlczovdmFyL3d3dy9odG1sJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NMQVNTSUNQUkVTUwogICAgICAtIENMQVNTSUNQUkVTU19EQl9IT1NUPW15c3FsCiAgICAgIC0gQ0xBU1NJQ1BSRVNTX0RCX1VTRVI9JFNFUlZJQ0VfVVNFUl9DTEFTU0lDUFJFU1MKICAgICAgLSBDTEFTU0lDUFJFU1NfREJfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfQ0xBU1NJQ1BSRVNTCiAgICAgIC0gQ0xBU1NJQ1BSRVNTX0RCX05BTUU9Y2xhc3NpY3ByZXNzCiAgICBkZXBlbmRzX29uOgogICAgICAtIG15c3FsCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjEnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTAKICBtYXJpYWRiOgogICAgaW1hZ2U6ICdteXNxbDo1LjcnCiAgICB2b2x1bWVzOgogICAgICAtICdteXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTVlTUUxfUk9PVF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9ST09UCiAgICAgIC0gTVlTUUxfREFUQUJBU0U9Y2xhc3NpY3ByZXNzCiAgICAgIC0gTVlTUUxfVVNFUj0kU0VSVklDRV9VU0VSX0NMQVNTSUNQUkVTUwogICAgICAtIE1ZU1FMX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX0NMQVNTSUNQUkVTUwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIG15c3FsYWRtaW4KICAgICAgICAtIHBpbmcKICAgICAgICAtICctaCcKICAgICAgICAtIDEyNy4wLjAuMQogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["cms","blog","content","management"],"logo":"svgs\/classicpress.svg","minversion":"0.0.0"},"classicpress-without-database":{"documentation":"https:\/\/www.classicpress.net\/?utm_source=coolify.io","slogan":"A lightweight, stable, instantly familiar free open-source content management system, based on WordPress without the block editor (Gutenberg).","compose":"c2VydmljZXM6CiAgY2xhc3NpY3ByZXNzOgogICAgaW1hZ2U6ICdjbGFzc2ljcHJlc3MvY2xhc3NpY3ByZXNzOmxhdGVzdCcKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2NsYXNzaWNwcmVzcy1maWxlczovdmFyL3d3dy9odG1sJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NMQVNTSUNQUkVTUwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["cms","blog","content","management"],"logo":"svgs\/classicpress.svg","minversion":"0.0.0"},"cloudflared":{"documentation":"https:\/\/developers.cloudflare.com\/cloudflare-one\/connections\/connect-networks\/?utm_source=coolify.io","slogan":"Client for Cloudflare Tunnel, a daemon that exposes private services through the Cloudflare edge.","compose":"c2VydmljZXM6CiAgY2xvdWRmbGFyZWQ6CiAgICBjb250YWluZXJfbmFtZTogY2xvdWRmbGFyZS10dW5uZWwKICAgIGltYWdlOiAnY2xvdWRmbGFyZS9jbG91ZGZsYXJlZDpsYXRlc3QnCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgY29tbWFuZDogJ3R1bm5lbCBydW4nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBUVU5ORUxfVE9LRU49JENMT1VERkxBUkVfVFVOTkVMX1RPS0VOCg==","tags":null,"logo":"svgs\/cloudflared.svg","minversion":"0.0.0"},"code-server":{"documentation":"https:\/\/coder.com\/docs\/code-server\/latest?utm_source=coolify.io","slogan":"Code-Server is a web-based code editor that enables remote coding and collaboration from any device, anywhere.","compose":"c2VydmljZXM6CiAgY29kZS1zZXJ2ZXI6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvY29kZS1zZXJ2ZXI6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NPREVTRVJWRVJfODQ0MwogICAgICAtIFBVSUQ9MTAwMAogICAgICAtIFBHSUQ9MTAwMAogICAgICAtIFRaPUV1cm9wZS9NYWRyaWQKICAgICAgLSBQQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF82NF9QQVNTV09SRENPREVTRVJWRVIKICAgICAgLSBTVURPX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1NVRE9DT0RFU0VSVkVSCiAgICAgIC0gREVGQVVMVF9XT1JLU1BBQ0U9L2NvbmZpZy93b3Jrc3BhY2UKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2NvZGUtc2VydmVyLWNvbmZpZzovY29uZmlnJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjg0NDMnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["code","editor","remote","collaboration"],"logo":"svgs\/code-server.svg","minversion":"0.0.0","port":"8443"},"dashboard":{"documentation":"https:\/\/github.com\/phntxx\/dashboard?tab=readme-ov-file#dashboard?utm_source=coolify.io","slogan":"A dashboard, inspired by SUI.","compose":"c2VydmljZXM6CiAgZGFzaGJvYXJkOgogICAgaW1hZ2U6ICdwaG50eHgvZGFzaGJvYXJkOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9EQVNIQk9BUkRfODA4MAogICAgdm9sdW1lczoKICAgICAgLSAnZGFzaGJvYXJkLWRhdGE6L2FwcC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["dashboard","web","search","bookmarks"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"8080"},"directus-with-postgresql":{"documentation":"https:\/\/directus.io?utm_source=coolify.io","slogan":"Directus wraps databases with a dynamic API, and provides an intuitive app for managing its content.","compose":"c2VydmljZXM6CiAgZGlyZWN0dXM6CiAgICBpbWFnZTogJ2RpcmVjdHVzL2RpcmVjdHVzOjEwJwogICAgdm9sdW1lczoKICAgICAgLSAnZGlyZWN0dXMtdXBsb2FkczovZGlyZWN0dXMvdXBsb2FkcycKICAgICAgLSAnZGlyZWN0dXMtZXh0ZW5zaW9uczovZGlyZWN0dXMvZXh0ZW5zaW9ucycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9ESVJFQ1RVU184MDU1CiAgICAgIC0gS0VZPSRTRVJWSUNFX0JBU0U2NF82NF9LRVkKICAgICAgLSBTRUNSRVQ9JFNFUlZJQ0VfQkFTRTY0XzY0X1NFQ1JFVAogICAgICAtICdBRE1JTl9FTUFJTD0ke0FETUlOX0VNQUlMOi1hZG1pbkBleGFtcGxlLmNvbX0nCiAgICAgIC0gQURNSU5fUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfQURNSU4KICAgICAgLSBEQl9DTElFTlQ9cG9zdGdyZXMKICAgICAgLSBEQl9IT1NUPXBvc3RncmVzcWwKICAgICAgLSBEQl9QT1JUPTU0MzIKICAgICAgLSAnREJfREFUQUJBU0U9JHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1kaXJlY3R1c30nCiAgICAgIC0gREJfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTUUwKICAgICAgLSBEQl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMCiAgICAgIC0gUkVESVNfSE9TVD1yZWRpcwogICAgICAtIFJFRElTX1BPUlQ9NjM3OQogICAgICAtIFdFQlNPQ0tFVFNfRU5BQkxFRD10cnVlCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gd2dldAogICAgICAgIC0gJy1xJwogICAgICAgIC0gJy0tc3BpZGVyJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODA1NS9hZG1pbi9sb2dpbicKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICByZWRpczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2RpcmVjdHVzLXBvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWRpcmVjdHVzfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6Ny1hbHBpbmUnCiAgICBjb21tYW5kOiAncmVkaXMtc2VydmVyIC0tYXBwZW5kb25seSB5ZXMnCiAgICB2b2x1bWVzOgogICAgICAtICdkaXJlY3R1cy1yZWRpcy1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["directus","cms","database","sql"],"logo":"svgs\/directus.svg","minversion":"0.0.0","port":"8055"},"directus":{"documentation":"https:\/\/directus.io?utm_source=coolify.io","slogan":"Directus wraps databases with a dynamic API, and provides an intuitive app for managing its content.","compose":"c2VydmljZXM6CiAgZGlyZWN0dXM6CiAgICBpbWFnZTogJ2RpcmVjdHVzL2RpcmVjdHVzOjEwJwogICAgdm9sdW1lczoKICAgICAgLSAnZGlyZWN0dXMtdXBsb2FkczovZGlyZWN0dXMvdXBsb2FkcycKICAgICAgLSAnZGlyZWN0dXMtZGF0YWJhc2U6L2RpcmVjdHVzL2RhdGFiYXNlJwogICAgICAtICdkaXJlY3R1cy1leHRlbnNpb25zOi9kaXJlY3R1cy9leHRlbnNpb25zJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0RJUkVDVFVTXzgwNTUKICAgICAgLSBLRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X0tFWQogICAgICAtIFNFQ1JFVD0kU0VSVklDRV9CQVNFNjRfNjRfU0VDUkVUCiAgICAgIC0gJ0FETUlOX0VNQUlMPSR7QURNSU5fRU1BSUw6LWFkbWluQGV4YW1wbGUuY29tfScKICAgICAgLSBBRE1JTl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9BRE1JTgogICAgICAtIERCX0NMSUVOVD1zcWxpdGUzCiAgICAgIC0gREJfRklMRU5BTUU9L2RpcmVjdHVzL2RhdGFiYXNlL2RhdGEuZGIKICAgICAgLSBXRUJTT0NLRVRTX0VOQUJMRUQ9dHJ1ZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHdnZXQKICAgICAgICAtICctcScKICAgICAgICAtICctLXNwaWRlcicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwNTUvYWRtaW4vbG9naW4nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["directus","cms","database","sql"],"logo":"svgs\/directus.svg","minversion":"0.0.0","port":"8055"},"docker-registry":{"documentation":"https:\/\/docs.docker.com\/registry\/?utm_source=coolify.io","slogan":"The Docker Registry is lets you distribute Docker images.","compose":"c2VydmljZXM6CiAgcmVnaXN0cnk6CiAgICBpbWFnZTogJ3JlZ2lzdHJ5OjInCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fUkVHSVNUUllfNTAwMAogICAgICAtIFJFR0lTVFJZX0FVVEg9aHRwYXNzd2QKICAgICAgLSBSRUdJU1RSWV9BVVRIX0hUUEFTU1dEX1JFQUxNPVJlZ2lzdHJ5CiAgICAgIC0gUkVHSVNUUllfQVVUSF9IVFBBU1NXRF9QQVRIPS9hdXRoL3JlZ2lzdHJ5LnBhc3N3b3JkCiAgICAgIC0gUkVHSVNUUllfU1RPUkFHRV9GSUxFU1lTVEVNX1JPT1RESVJFQ1RPUlk9L2RhdGEKICAgIHZvbHVtZXM6CiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2F1dGgvcmVnaXN0cnkucGFzc3dvcmQKICAgICAgICB0YXJnZXQ6IC9hdXRoL3JlZ2lzdHJ5LnBhc3N3b3JkCiAgICAgICAgaXNEaXJlY3Rvcnk6IGZhbHNlCiAgICAgICAgY29udGVudDogJ3Rlc3R1c2VyOiQyeSQwNSQvbzJKdm1JMmJoRXhYSXQ2T3F4YTdla1lCN3Yzc2NqMXdGRWY2dEJzbEp2Sk9Nb1BRTC5HeScKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vY29uZmlnL2NvbmZpZy55bWwKICAgICAgICB0YXJnZXQ6IC9ldGMvZG9ja2VyL3JlZ2lzdHJ5L2NvbmZpZy55bWwKICAgICAgICBpc0RpcmVjdG9yeTogZmFsc2UKICAgICAgICBjb250ZW50OiAidmVyc2lvbjogMC4xXG5sb2c6XG4gIGZpZWxkczpcbiAgICBzZXJ2aWNlOiByZWdpc3RyeVxuc3RvcmFnZTpcbiAgY2FjaGU6XG4gICAgYmxvYmRlc2NyaXB0b3I6IGlubWVtb3J5XG4gIGZpbGVzeXN0ZW06XG4gICAgcm9vdGRpcmVjdG9yeTogL3Zhci9saWIvcmVnaXN0cnlcbmh0dHA6XG4gIGFkZHI6IDo1MDAwXG4gIGhlYWRlcnM6XG4gICAgWC1Db250ZW50LVR5cGUtT3B0aW9uczogW25vc25pZmZdXG5oZWFsdGg6XG4gIHN0b3JhZ2Vkcml2ZXI6XG4gICAgZW5hYmxlZDogdHJ1ZVxuICAgIGludGVydmFsOiAxMHNcbiAgICB0aHJlc2hvbGQ6IDMiCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2RhdGEKICAgICAgICB0YXJnZXQ6IC9kYXRhCiAgICAgICAgaXNEaXJlY3Rvcnk6IHRydWUK","tags":["registry","images","docker"],"logo":"svgs\/docker-registry.png","minversion":"0.0.0","port":"5000"},"docuseal-with-postgres":{"documentation":"https:\/\/www.docuseal.co\/?utm_source=coolify.io","slogan":"Document Signing for Everyone free forever for individuals, extensible for businesses and developers. Open Source Alternative to DocuSign, PandaDoc and more.","compose":"c2VydmljZXM6CiAgZG9jdXNlYWw6CiAgICBpbWFnZTogJ2RvY3VzZWFsL2RvY3VzZWFsOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9ET0NVU0VBTF8zMDAwCiAgICAgIC0gJ0hPU1Q9JHtTRVJWSUNFX0ZRRE5fRE9DVVNFQUx9JwogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXNxbDovLyRTRVJWSUNFX1VTRVJfUE9TVEdSRVM6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNAcG9zdGdyZXNxbDo1NDMyLyR7UE9TVEdSRVNfREJ9JwogICAgdm9sdW1lczoKICAgICAgLSAnZG9jdXNlYWwtZGF0YTovZGF0YScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLXEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWRvY3VzZWFsfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["documentation"],"logo":"svgs\/docuseal.png","minversion":"0.0.0","port":"3000"},"docuseal":{"documentation":"https:\/\/www.docuseal.co\/?utm_source=coolify.io","slogan":"Document Signing for Everyone free forever for individuals, extensible for businesses and developers. Open Source Alternative to DocuSign, PandaDoc and more.","compose":"c2VydmljZXM6CiAgZG9jdXNlYWw6CiAgICBpbWFnZTogJ2RvY3VzZWFsL2RvY3VzZWFsOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9ET0NVU0VBTF8zMDAwCiAgICAgIC0gJ0hPU1Q9JHtTRVJWSUNFX0ZRRE5fRE9DVVNFQUx9JwogICAgdm9sdW1lczoKICAgICAgLSAnZG9jdXNlYWwtZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLXEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["documentation"],"logo":"svgs\/docuseal.png","minversion":"0.0.0","port":"3000"},"dokuwiki":{"documentation":"https:\/\/www.dokuwiki.org\/?utm_source=coolify.io","slogan":"A lightweight and easy-to-use wiki platform for creating and managing documentation and knowledge bases.","compose":"c2VydmljZXM6CiAgZG9rdXdpa2k6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvZG9rdXdpa2k6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0RPS1VXSUtJCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZG9rdXdpa2ktY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["wiki","documentation","knowledge","base"],"logo":"svgs\/dokuwiki.png","minversion":"0.0.0"},"duplicati":{"documentation":"https:\/\/duplicati.readthedocs.io?utm_source=coolify.io","slogan":"Duplicati is a backup solution, allowing you to make scheduled backups with encryption.","compose":"c2VydmljZXM6CiAgZHVwbGljYXRpOgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL2R1cGxpY2F0aTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fRFVQTElDQVRJXzgyMDAKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICB2b2x1bWVzOgogICAgICAtICdkdXBsaWNhdGktY29uZmlnOi9jb25maWcnCiAgICAgIC0gJ2R1cGxpY2F0aS1iYWNrdXBzOi9iYWNrdXBzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgyMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["backup","encryption"],"logo":"svgs\/duplicati.webp","minversion":"0.0.0","port":"8200"},"emby":{"documentation":"https:\/\/emby.media\/support\/articles\/Home.html?utm_source=coolify.io","slogan":"A media server software that allows you to organize, stream, and access your multimedia content effortlessly.","compose":"c2VydmljZXM6CiAgZW1ieToKICAgIGltYWdlOiAnbHNjci5pby9saW51eHNlcnZlci9lbWJ5OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9FTUJZXzgwOTYKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICB2b2x1bWVzOgogICAgICAtICdlbWJ5LWNvbmZpZzovY29uZmlnJwogICAgICAtICdlbWJ5LXR2c2hvd3M6L3R2c2hvd3MnCiAgICAgIC0gJ2VtYnktbW92aWVzOi9tb3ZpZXMnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODA5NicKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=","tags":["media","server","movies","tv","music"],"logo":"svgs\/emby.png","minversion":"0.0.0","port":"8096"},"embystat":{"documentation":"https:\/\/github.com\/mregni\/EmbyStat?utm_source=coolify.io","slogan":"EmnyStat is a web analytics tool, designed to provide insight into website traffic and user behavior.","compose":"c2VydmljZXM6CiAgZW1ieXN0YXQ6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvZW1ieXN0YXQ6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0VNQllTVEFUXzY1NTUKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICB2b2x1bWVzOgogICAgICAtICdlbWJ5c3RhdC1jb25maWc6L2NvbmZpZycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo2NTU1JwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==","tags":["media","server","movies","tv","music"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"6555"},"fider":{"documentation":"https:\/\/fider.io?utm_source=coolify.io","slogan":"Fider is a feedback platform for collecting and managing user feedback.","compose":"c2VydmljZXM6CiAgZmlkZXI6CiAgICBpbWFnZTogJ2dldGZpZGVyL2ZpZGVyOnN0YWJsZScKICAgIGVudmlyb25tZW50OgogICAgICBCQVNFX1VSTDogJFNFUlZJQ0VfRlFETl9GSURFUl8zMDAwCiAgICAgIERBVEFCQVNFX1VSTDogJ3Bvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFUzokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0BkYXRhYmFzZTo1NDMyL2ZpZGVyP3NzbG1vZGU9ZGlzYWJsZScKICAgICAgSldUX1NFQ1JFVDogJFNFUlZJQ0VfUEFTU1dPUkRfNjRfRklERVIKICAgICAgRU1BSUxfTk9SRVBMWTogJyR7RU1BSUxfTk9SRVBMWTotbm9yZXBseUBleGFtcGxlLmNvbX0nCiAgICAgIEVNQUlMX01BSUxHVU5fQVBJOiAkRU1BSUxfTUFJTEdVTl9BUEkKICAgICAgRU1BSUxfTUFJTEdVTl9ET01BSU46ICRFTUFJTF9NQUlMR1VOX0RPTUFJTgogICAgICBFTUFJTF9NQUlMR1VOX1JFR0lPTjogJEVNQUlMX01BSUxHVU5fUkVHSU9OCiAgICAgIEVNQUlMX1NNVFBfSE9TVDogJyR7RU1BSUxfU01UUF9IT1NUOi1zbXRwLm1haWxndW4uY29tfScKICAgICAgRU1BSUxfU01UUF9QT1JUOiAnJHtFTUFJTF9TTVRQX1BPUlQ6LTU4N30nCiAgICAgIEVNQUlMX1NNVFBfVVNFUk5BTUU6ICcke0VNQUlMX1NNVFBfVVNFUk5BTUU6LXBvc3RtYXN0ZXJAbWFpbGd1bi5jb219JwogICAgICBFTUFJTF9TTVRQX1BBU1NXT1JEOiAkRU1BSUxfU01UUF9QQVNTV09SRAogICAgICBFTUFJTF9TTVRQX0VOQUJMRV9TVEFSVFRMUzogJEVNQUlMX1NNVFBfRU5BQkxFX1NUQVJUVExTCiAgICAgIEVNQUlMX0FXU1NFU19SRUdJT046ICRFTUFJTF9BV1NTRVNfUkVHSU9OCiAgICAgIEVNQUlMX0FXU1NFU19BQ0NFU1NfS0VZX0lEOiAkRU1BSUxfQVdTU0VTX0FDQ0VTU19LRVlfSUQKICAgICAgRU1BSUxfQVdTU0VTX1NFQ1JFVF9BQ0NFU1NfS0VZOiAkRU1BSUxfQVdTU0VTX1NFQ1JFVF9BQ0NFU1NfS0VZCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gL2FwcC9maWRlcgogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDEwCiAgZGF0YWJhc2U6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjEyJwogICAgdm9sdW1lczoKICAgICAgLSAncGdfZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgUE9TVEdSRVNfVVNFUjogJFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICBQT1NUR1JFU19QQVNTV09SRDogJFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgUE9TVEdSRVNfREI6ICcke1BPU1RHUkVTX0RCOi1maWRlcn0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcGdfaXNyZWFkeQogICAgICAgIC0gJy1VJwogICAgICAgIC0gJFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["feedback","user-feedback"],"logo":"svgs\/fider.svg","minversion":"0.0.0","port":"3000"},"filebrowser":{"documentation":"https:\/\/filebrowser.org?utm_source=coolify.io","slogan":"FileBrowser is a web-based file manager and file explorer with a user-friendly interface.","compose":"c2VydmljZXM6CiAgZmlsZWJyb3dzZXI6CiAgICBpbWFnZTogJ2ZpbGVicm93c2VyL2ZpbGVicm93c2VyOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9GSUxFQlJPV1NFUgogICAgdm9sdW1lczoKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vc3J2CiAgICAgICAgdGFyZ2V0OiAvc3J2CiAgICAgICAgaXNEaXJlY3Rvcnk6IHRydWUKICAgICAgLSAnLi9kYXRhYmFzZS5kYjovZGF0YWJhc2UuZGInCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2ZpbGVicm93c2VyLmpzb24KICAgICAgICB0YXJnZXQ6IC8uZmlsZWJyb3dzZXIuanNvbgogICAgICAgIHJlYWRfb25seTogdHJ1ZQogICAgICAgIGNvbnRlbnQ6ICd7fScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=","tags":["file-management","storage-access","data-organization","file-utilization","administration-tool"],"logo":"svgs\/filebrowser.svg","minversion":"0.0.0"},"firefly":{"documentation":"https:\/\/firefly-iii.org?utm_source=coolify.io","slogan":"A personal finances manager that can help you save money.","compose":"c2VydmljZXM6CiAgZmlyZWZseToKICAgIGltYWdlOiAnZmlyZWZseWlpaS9jb3JlOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9GSVJFRkxZXzgwODAKICAgICAgLSBBUFBfS0VZPSRTRVJWSUNFX0JBU0U2NF9BUFBLRVkKICAgICAgLSBEQl9IT1NUPW15c3FsCiAgICAgIC0gREJfUE9SVD0zMzA2CiAgICAgIC0gREJfQ09OTkVDVElPTj1teXNxbAogICAgICAtICdEQl9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFOi1maXJlZmx5fScKICAgICAgLSBEQl9VU0VSTkFNRT0kU0VSVklDRV9VU0VSX01ZU1FMCiAgICAgIC0gREJfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfTVlTUUwKICAgICAgLSBTVEFUSUNfQ1JPTl9UT0tFTj0kU0VSVklDRV9CQVNFNjRfQ1JPTlRPS0VOCiAgICAgIC0gJ1RSVVNURURfUFJPWElFUz0qJwogICAgdm9sdW1lczoKICAgICAgLSAnZmlyZWZseS11cGxvYWQ6L3Zhci93d3cvaHRtbC9zdG9yYWdlL3VwbG9hZCcKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MDgwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgICBkZXBlbmRzX29uOgogICAgICBteXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogIG15c3FsOgogICAgaW1hZ2U6ICdtYXJpYWRiOmx0cycKICAgIGVudmlyb25tZW50OgogICAgICAtICdNWVNRTF9VU0VSPSR7U0VSVklDRV9VU0VSX01ZU1FMfScKICAgICAgLSAnTVlTUUxfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMfScKICAgICAgLSAnTVlTUUxfREFUQUJBU0U9JHtNWVNRTF9EQVRBQkFTRTotZmlyZWZseX0nCiAgICAgIC0gJ01ZU1FMX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMUk9PVH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gbWFyaWFkYi1hZG1pbgogICAgICAgIC0gcGluZwogICAgICAgIC0gJy1oJwogICAgICAgIC0gMTI3LjAuMC4xCiAgICAgICAgLSAnLXVyb290JwogICAgICAgIC0gJy1wJHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMUk9PVH0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2ZpcmVmbHktbXlzcWwtZGF0YTovdmFyL2xpYi9teXNxbCcKICBjcm9uOgogICAgaW1hZ2U6IGFscGluZQogICAgZW50cnlwb2ludDoKICAgICAgLSAvZW50cnlwb2ludC5zaAogICAgdm9sdW1lczoKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vZW50cnlwb2ludC5zaAogICAgICAgIHRhcmdldDogL2VudHJ5cG9pbnQuc2gKICAgICAgICBjb250ZW50OiAiIyEvYmluL3NoXG4jIFN1YnN0aXR1dGUgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlIGludG8gdGhlIGNyb24gY29tbWFuZFxuQ1JPTl9DT01NQU5EPVwiMCAzICogKiAqIHdnZXQgLXFPLSBodHRwOi8vZmlyZWZseTo4MDgwL2FwaS92MS9jcm9uLyR7U1RBVElDX0NST05fVE9LRU59XCJcbiMgQWRkIHRoZSBjcm9uIGNvbW1hbmQgdG8gdGhlIGNyb250YWJcbmVjaG8gXCIkQ1JPTl9DT01NQU5EXCIgfCBjcm9udGFiIC1cbiMgU3RhcnQgdGhlIGNyb24gZGFlbW9uIGluIHRoZSBmb3JlZ3JvdW5kIHdpdGggbG9nZ2luZyB0byBzdGRvdXRcbmNyb25kIC1mIC1MIC9kZXYvc3Rkb3V0IgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU1RBVElDX0NST05fVE9LRU49JFNFUlZJQ0VfQkFTRTY0X0NST05UT0tFTgo=","tags":["finance","money","personal","manager"],"logo":"svgs\/firefly.svg","minversion":"0.0.0","port":"8080"},"formbricks":{"documentation":"https:\/\/formbricks.com?utm_source=coolify.io","slogan":"Open Source Experience Management","compose":"c2VydmljZXM6CiAgZm9ybWJyaWNrczoKICAgIGltYWdlOiAnZ2hjci5pby9mb3JtYnJpY2tzL2Zvcm1icmlja3M6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0ZPUk1CUklDS1NfMzAwMAogICAgICAtIFdFQkFQUF9VUkw9JFNFUlZJQ0VfRlFETl9GT1JNQlJJQ0tTCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyRTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTDokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMQHBvc3RncmVzcWw6NTQzMi8ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWZvcm1icmlja3N9JwogICAgICAtIE5FWFRBVVRIX1NFQ1JFVD0kU0VSVklDRV9CQVNFNjRfNjRfTkVYVEFVVEgKICAgICAgLSBORVhUQVVUSF9VUkw9JFNFUlZJQ0VfRlFETl9GT1JNQlJJQ0tTCiAgICAgIC0gRU5DUllQVElPTl9LRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X0VOQ1JZUFRJT04KICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdNQUlMX0ZST009JHtNQUlMX0ZST006LXRlc3RAZXhhbXBsZS5jb219JwogICAgICAtICdTTVRQX0hPU1Q9JHtTTVRQX0hPU1Q6LXRlc3QuZXhhbXBsZS5jb219JwogICAgICAtICdTTVRQX1BPUlQ9JHtTTVRQX1BPUlQ6LTU4N30nCiAgICAgIC0gJ1NNVFBfVVNFUj0ke1NNVFBfVVNFUjotdGVzdH0nCiAgICAgIC0gJ1NNVFBfUEFTU1dPUkQ9JHtTTVRQX1BBU1NXT1JEOi10ZXN0fScKICAgICAgLSAnU01UUF9TRUNVUkVfRU5BQkxFRD0ke1NNVFBfU0VDVVJFX0VOQUJMRUQ6LTB9JwogICAgICAtICdTSE9SVF9VUkxfQkFTRT0ke1NIT1JUX1VSTF9CQVNFfScKICAgICAgLSAnRU1BSUxfVkVSSUZJQ0FUSU9OX0RJU0FCTEVEPSR7RU1BSUxfVkVSSUZJQ0FUSU9OX0RJU0FCTEVEOi0xfScKICAgICAgLSAnUEFTU1dPUkRfUkVTRVRfRElTQUJMRUQ9JHtQQVNTV09SRF9SRVNFVF9ESVNBQkxFRDotMX0nCiAgICAgIC0gJ1NJR05VUF9ESVNBQkxFRD0ke1NJR05VUF9ESVNBQkxFRDotMH0nCiAgICAgIC0gJ0lOVklURV9ESVNBQkxFRD0ke0lOVklURV9ESVNBQkxFRDotMH0nCiAgICAgIC0gJ1BSSVZBQ1lfVVJMPSR7UFJJVkFDWV9VUkx9JwogICAgICAtICdURVJNU19VUkw9JHtURVJNU19VUkx9JwogICAgICAtICdJTVBSSU5UX1VSTD0ke0lNUFJJTlRfVVJMfScKICAgICAgLSAnR0lUSFVCX0FVVEhfRU5BQkxFRD0ke0dJVEhVQl9BVVRIX0VOQUJMRUQ6LTB9JwogICAgICAtICdHSVRIVUJfSUQ9JHtHSVRIVUJfSUR9JwogICAgICAtICdHSVRIVUJfU0VDUkVUPSR7R0lUSFVCX1NFQ1JFVH0nCiAgICAgIC0gJ0dPT0dMRV9BVVRIX0VOQUJMRUQ9JHtHT09HTEVfQVVUSF9FTkFCTEVEOi0wfScKICAgICAgLSAnR09PR0xFX0NMSUVOVF9JRD0ke0dPT0dMRV9DTElFTlRfSUR9JwogICAgICAtICdHT09HTEVfQ0xJRU5UX1NFQ1JFVD0ke0dPT0dMRV9DTElFTlRfU0VDUkVUfScKICAgICAgLSAnQVNTRVRfUFJFRklYX1VSTD0ke0FTU0VUX1BSRUZJWF9VUkx9JwogICAgdm9sdW1lczoKICAgICAgLSAnZm9ybWJyaWNrcy11cGxvYWRzOi9hcHBzL3dlYi91cGxvYWRzLycKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAnZm9ybWJyaWNrcy1wb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1mb3JtYnJpY2tzfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["form","builder","forms","open source","experience","management","self-hosted","docker"],"logo":"svgs\/formbricks.png","minversion":"0.0.0","port":"3000"},"ghost":{"documentation":"https:\/\/ghost.org?utm_source=coolify.io","slogan":"Ghost is a content management system (CMS) and blogging platform.","compose":"c2VydmljZXM6CiAgZ2hvc3Q6CiAgICBpbWFnZTogJ2dob3N0OjUnCiAgICB2b2x1bWVzOgogICAgICAtICdnaG9zdC1jb250ZW50LWRhdGE6L3Zhci9saWIvZ2hvc3QvY29udGVudCcKICAgIGVudmlyb25tZW50OgogICAgICAtIHVybD0kU0VSVklDRV9GUUROX0dIT1NUXzIzNjgKICAgICAgLSBkYXRhYmFzZV9fY2xpZW50PW15c3FsCiAgICAgIC0gZGF0YWJhc2VfX2Nvbm5lY3Rpb25fX2hvc3Q9bXlzcWwKICAgICAgLSBkYXRhYmFzZV9fY29ubmVjdGlvbl9fdXNlcj0kU0VSVklDRV9VU0VSX01ZU1FMCiAgICAgIC0gZGF0YWJhc2VfX2Nvbm5lY3Rpb25fX3Bhc3N3b3JkPSRTRVJWSUNFX1BBU1NXT1JEX01ZU1FMCiAgICAgIC0gJ2RhdGFiYXNlX19jb25uZWN0aW9uX19kYXRhYmFzZT0ke01ZU1FMX0RBVEFCQVNFLWdob3N0fScKICAgICAgLSBtYWlsX190cmFuc3BvcnQ9U01UUAogICAgICAtICdtYWlsX19vcHRpb25zX19hdXRoX19wYXNzPSR7TUFJTF9PUFRJT05TX0FVVEhfUEFTU30nCiAgICAgIC0gJ21haWxfX29wdGlvbnNfX2F1dGhfX3VzZXI9JHtNQUlMX09QVElPTlNfQVVUSF9VU0VSfScKICAgICAgLSAnbWFpbF9fb3B0aW9uc19fc2VjdXJlPSR7TUFJTF9PUFRJT05TX1NFQ1VSRTotdHJ1ZX0nCiAgICAgIC0gJ21haWxfX29wdGlvbnNfX3BvcnQ9JHtNQUlMX09QVElPTlNfUE9SVDotNDY1fScKICAgICAgLSAnbWFpbF9fb3B0aW9uc19fc2VydmljZT0ke01BSUxfT1BUSU9OU19TRVJWSUNFOi1NYWlsZ3VufScKICAgICAgLSAnbWFpbF9fb3B0aW9uc19faG9zdD0ke01BSUxfT1BUSU9OU19IT1NUfScKICAgIGRlcGVuZHNfb246CiAgICAgIG15c3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gZWNobwogICAgICAgIC0gb2sKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo4LjAnCiAgICB2b2x1bWVzOgogICAgICAtICdnaG9zdC1teXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtTRVJWSUNFX1VTRVJfTVlTUUx9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUx9JwogICAgICAtICdNWVNRTF9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFfScKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUxST09UfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBteXNxbGFkbWluCiAgICAgICAgLSBwaW5nCiAgICAgICAgLSAnLWgnCiAgICAgICAgLSAxMjcuMC4wLjEKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["cms","blog","content","management","system"],"logo":"svgs\/ghost.svg","minversion":"0.0.0","port":"2368"},"gitea-with-mariadb":{"documentation":"https:\/\/docs.gitea.com?utm_source=coolify.io","slogan":"Gitea is a self-hosted, lightweight Git service, offering version control, collaboration, and code hosting.","compose":"c2VydmljZXM6CiAgZ2l0ZWE6CiAgICBpbWFnZTogJ2dpdGVhL2dpdGVhOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9HSVRFQV8zMDAwCiAgICAgIC0gVVNFUl9VSUQ9MTAwMAogICAgICAtIFVTRVJfR0lEPTEwMDAKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX0RCX1RZUEU9bXlzcWwKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX0hPU1Q9bWFyaWFkYgogICAgICAtICdHSVRFQV9fZGF0YWJhc2VfX05BTUU9JHtNWVNRTF9EQVRBQkFTRS1naXRlYX0nCiAgICAgIC0gR0lURUFfX2RhdGFiYXNlX19VU0VSPSRTRVJWSUNFX1VTRVJfTVlTUUwKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX1BBU1NXRD0kU0VSVklDRV9QQVNTV09SRF9NWVNRTAogICAgdm9sdW1lczoKICAgICAgLSAnZ2l0ZWEtZGF0YTovdmFyL2xpYi9naXRlYScKICAgICAgLSAnZ2l0ZWEtdGltZXpvbmU6L2V0Yy90aW1lem9uZTpybycKICAgICAgLSAnZ2l0ZWEtbG9jYWx0aW1lOi9ldGMvbG9jYWx0aW1lOnJvJwogICAgcG9ydHM6CiAgICAgIC0gJzIyMjIyOjIyJwogICAgZGVwZW5kc19vbjoKICAgICAgbWFyaWFkYjoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjMwMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICBtYXJpYWRiOgogICAgaW1hZ2U6ICdtYXJpYWRiOjExJwogICAgdm9sdW1lczoKICAgICAgLSAnZ2l0ZWEtbWFyaWFkYi1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtTRVJWSUNFX1VTRVJfTVlTUUx9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUx9JwogICAgICAtICdNWVNRTF9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFfScKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUxST09UfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBoZWFsdGhjaGVjay5zaAogICAgICAgIC0gJy0tY29ubmVjdCcKICAgICAgICAtICctLWlubm9kYl9pbml0aWFsaXplZCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["version control","collaboration","code","hosting","lightweight","mariadb"],"logo":"svgs\/gitea.svg","minversion":"0.0.0"},"gitea-with-mysql":{"documentation":"https:\/\/docs.gitea.com?utm_source=coolify.io","slogan":"Gitea is a self-hosted, lightweight Git service, offering version control, collaboration, and code hosting.","compose":"c2VydmljZXM6CiAgZ2l0ZWE6CiAgICBpbWFnZTogJ2dpdGVhL2dpdGVhOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9HSVRFQV8zMDAwCiAgICAgIC0gVVNFUl9VSUQ9MTAwMAogICAgICAtIFVTRVJfR0lEPTEwMDAKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX0RCX1RZUEU9bXlzcWwKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX0hPU1Q9bXlzcWwKICAgICAgLSAnR0lURUFfX2RhdGFiYXNlX19OQU1FPSR7TVlTUUxfREFUQUJBU0UtZ2l0ZWF9JwogICAgICAtIEdJVEVBX19kYXRhYmFzZV9fVVNFUj0kU0VSVklDRV9VU0VSX01ZU1FMCiAgICAgIC0gR0lURUFfX2RhdGFiYXNlX19QQVNTV0Q9JFNFUlZJQ0VfUEFTU1dPUkRfTVlTUUwKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2dpdGVhLWRhdGE6L3Zhci9saWIvZ2l0ZWEnCiAgICAgIC0gJ2dpdGVhLXRpbWV6b25lOi9ldGMvdGltZXpvbmU6cm8nCiAgICAgIC0gJ2dpdGVhLWxvY2FsdGltZTovZXRjL2xvY2FsdGltZTpybycKICAgIHBvcnRzOgogICAgICAtICcyMjIyMjoyMicKICAgIGRlcGVuZHNfb246CiAgICAgIG15c3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo4LjAnCiAgICB2b2x1bWVzOgogICAgICAtICdnaXRlYS1teXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtTRVJWSUNFX1VTRVJfTVlTUUx9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUx9JwogICAgICAtICdNWVNRTF9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFfScKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUxST09UfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBteXNxbGFkbWluCiAgICAgICAgLSBwaW5nCiAgICAgICAgLSAnLWgnCiAgICAgICAgLSAxMjcuMC4wLjEKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["version control","collaboration","code","hosting","lightweight","mysql"],"logo":"svgs\/gitea.svg","minversion":"0.0.0"},"gitea-with-postgresql":{"documentation":"https:\/\/docs.gitea.com?utm_source=coolify.io","slogan":"Gitea is a self-hosted, lightweight Git service, offering version control, collaboration, and code hosting.","compose":"c2VydmljZXM6CiAgZ2l0ZWE6CiAgICBpbWFnZTogJ2dpdGVhL2dpdGVhOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9HSVRFQV8zMDAwCiAgICAgIC0gVVNFUl9VSUQ9MTAwMAogICAgICAtIFVTRVJfR0lEPTEwMDAKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX0RCX1RZUEU9cG9zdGdyZXMKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX0hPU1Q9cG9zdGdyZXNxbAogICAgICAtICdHSVRFQV9fZGF0YWJhc2VfX05BTUU9JHtQT1NUR1JFU1FMX0RBVEFCQVNFLWdpdGVhfScKICAgICAgLSBHSVRFQV9fZGF0YWJhc2VfX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMCiAgICAgIC0gR0lURUFfX2RhdGFiYXNlX19QQVNTV0Q9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTAogICAgdm9sdW1lczoKICAgICAgLSAnZ2l0ZWEtZGF0YTovdmFyL2xpYi9naXRlYScKICAgICAgLSAnZ2l0ZWEtdGltZXpvbmU6L2V0Yy90aW1lem9uZTpybycKICAgICAgLSAnZ2l0ZWEtbG9jYWx0aW1lOi9ldGMvbG9jYWx0aW1lOnJvJwogICAgcG9ydHM6CiAgICAgIC0gJzIyMjIyOjIyJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjMwMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdnaXRlYS1wb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU1FMX0RBVEFCQVNFfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["version control","collaboration","code","hosting","lightweight","postgresql"],"logo":"svgs\/gitea.svg","minversion":"0.0.0"},"gitea":{"documentation":"https:\/\/docs.gitea.com?utm_source=coolify.io","slogan":"Gitea is a self-hosted, lightweight Git service, offering version control, collaboration, and code hosting.","compose":"c2VydmljZXM6CiAgZ2l0ZWE6CiAgICBpbWFnZTogJ2dpdGVhL2dpdGVhOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9HSVRFQV8zMDAwCiAgICAgIC0gVVNFUl9VSUQ9MTAwMAogICAgICAtIFVTRVJfR0lEPTEwMDAKICAgIHBvcnRzOgogICAgICAtICcyMjIyMjoyMicKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2dpdGVhLWRhdGE6L2RhdGEnCiAgICAgIC0gJ2dpdGVhLXRpbWV6b25lOi9ldGMvdGltZXpvbmU6cm8nCiAgICAgIC0gJ2dpdGVhLWxvY2FsdGltZTovZXRjL2xvY2FsdGltZTpybycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==","tags":["version control","collaboration","code","hosting","lightweight"],"logo":"svgs\/gitea.svg","minversion":"0.0.0"},"glance":{"documentation":"https:\/\/github.com\/glanceapp\/glance?utm_source=coolify.io","slogan":"A self-hosted dashboard that puts all your feeds in one place.","compose":"c2VydmljZXM6CiAgZ2xhbmNlOgogICAgaW1hZ2U6ICdnbGFuY2VhcHAvZ2xhbmNlOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9HTEFOQ0VfODA4MAogICAgdm9sdW1lczoKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vZ2xhbmNlLXNldHRpbmdzCiAgICAgICAgdGFyZ2V0OiAvYXBwL2dsYW5jZS55bWwKICAgICAgICBjb250ZW50OiAicGFnZXM6XG4gIC0gbmFtZTogSG9tZVxuICAgIHNlcnZlcjpcbiAgICAgIGhvc3Q6IDAuMC4wLjBcbiAgICAgIHBvcnQ6IDgwODBcbiAgICAgIGFzc2V0cy1wYXRoOiAvdXNlci9hc3NldHNcbiAgICBjb2x1bW5zOlxuICAgICAgLSBzaXplOiBzbWFsbFxuICAgICAgICB3aWRnZXRzOlxuICAgICAgICAgIC0gdHlwZTogY2FsZW5kYXJcblxuICAgICAgICAgIC0gdHlwZTogcnNzXG4gICAgICAgICAgICBsaW1pdDogMTBcbiAgICAgICAgICAgIGNvbGxhcHNlLWFmdGVyOiAzXG4gICAgICAgICAgICBjYWNoZTogM2hcbiAgICAgICAgICAgIGZlZWRzOlxuICAgICAgICAgICAgICAtIHVybDogaHR0cHM6Ly9jaWVjaGFub3cuc2tpL2F0b20ueG1sXG4gICAgICAgICAgICAgIC0gdXJsOiBodHRwczovL3d3dy5qb3Nod2NvbWVhdS5jb20vcnNzLnhtbFxuICAgICAgICAgICAgICAgIHRpdGxlOiBKb3NoIENvbWVhdVxuICAgICAgICAgICAgICAtIHVybDogaHR0cHM6Ly9zYW13aG8uZGV2L3Jzcy54bWxcbiAgICAgICAgICAgICAgLSB1cmw6IGh0dHBzOi8vYXdlc29tZWtsaW5nLmdpdGh1Yi5pby9mZWVkLnhtbFxuICAgICAgICAgICAgICAtIHVybDogaHR0cHM6Ly9pc2hhZGVlZC5jb20vZmVlZC54bWxcbiAgICAgICAgICAgICAgICB0aXRsZTogQWhtYWQgU2hhZGVlZFxuXG4gICAgICAgICAgLSB0eXBlOiB0d2l0Y2gtY2hhbm5lbHNcbiAgICAgICAgICAgIGNoYW5uZWxzOlxuICAgICAgICAgICAgICAtIHRoZXByaW1lYWdlblxuICAgICAgICAgICAgICAtIGhleWFuZHJhc1xuICAgICAgICAgICAgICAtIGNvaGhjYXJuYWdlXG4gICAgICAgICAgICAgIC0gY2hyaXN0aXR1c3RlY2hcbiAgICAgICAgICAgICAgLSBibHVyYnNcbiAgICAgICAgICAgICAgLSBhc21vbmdvbGRcbiAgICAgICAgICAgICAgLSBqZW1iYXdsc1xuXG4gICAgICAtIHNpemU6IGZ1bGxcbiAgICAgICAgd2lkZ2V0czpcbiAgICAgICAgICAtIHR5cGU6IGhhY2tlci1uZXdzXG5cbiAgICAgICAgICAtIHR5cGU6IHZpZGVvc1xuICAgICAgICAgICAgY2hhbm5lbHM6XG4gICAgICAgICAgICAgIC0gVUNSLURYYzF2b292UzhuaEF2Y2NSWmhnICMgSmVmZiBHZWVybGluZ1xuICAgICAgICAgICAgICAtIFVDdjZKX2pKYThHSnFGd1FOZ05yTXV3dyAjIFNlcnZlVGhlSG9tZVxuICAgICAgICAgICAgICAtIFVDT2stZ0h5amNXWk5qM0JyNG94d2gwQSAjIFRlY2hubyBUaW1cblxuICAgICAgICAgIC0gdHlwZTogcmVkZGl0XG4gICAgICAgICAgICBzdWJyZWRkaXQ6IHNlbGZob3N0ZWRcblxuICAgICAgLSBzaXplOiBzbWFsbFxuICAgICAgICB3aWRnZXRzOlxuICAgICAgICAgIC0gdHlwZTogd2VhdGhlclxuICAgICAgICAgICAgbG9jYXRpb246IExvbmRvbiwgVW5pdGVkIEtpbmdkb21cblxuICAgICAgICAgIC0gdHlwZTogc3RvY2tzXG4gICAgICAgICAgICBzdG9ja3M6XG4gICAgICAgICAgICAgIC0gc3ltYm9sOiBTUFlcbiAgICAgICAgICAgICAgICBuYW1lOiBTJlAgNTAwXG4gICAgICAgICAgICAgIC0gc3ltYm9sOiBCVEMtVVNEXG4gICAgICAgICAgICAgICAgbmFtZTogQml0Y29pblxuICAgICAgICAgICAgICAtIHN5bWJvbDogTlZEQVxuICAgICAgICAgICAgICAgIG5hbWU6IE5WSURJQVxuICAgICAgICAgICAgICAtIHN5bWJvbDogQUFQTFxuICAgICAgICAgICAgICAgIG5hbWU6IEFwcGxlXG4gICAgICAgICAgICAgIC0gc3ltYm9sOiBNU0ZUXG4gICAgICAgICAgICAgICAgbmFtZTogTWljcm9zb2Z0XG4gICAgICAgICAgICAgIC0gc3ltYm9sOiBHT09HTFxuICAgICAgICAgICAgICAgIG5hbWU6IEdvb2dsZVxuICAgICAgICAgICAgICAtIHN5bWJvbDogQU1EXG4gICAgICAgICAgICAgICAgbmFtZTogQU1EXG4gICAgICAgICAgICAgIC0gc3ltYm9sOiBSRERUXG4gICAgICAgICAgICAgICAgbmFtZTogUmVkZGl0IgogICAgICAtICdnbGFuY2UtYXNzZXRzOi91c2VyL2Fzc2V0cycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBlY2hvCiAgICAgICAgLSAnWytdIFNob3VsZCBiZSB3b3JraW5nIGZpbmUuJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["dashboard","server","applications","interface","rrss"],"logo":"svgs\/glance.png","minversion":"0.0.0","port":"8080"},"glances":{"documentation":"https:\/\/nicolargo.github.io\/glances\/?utm_source=coolify.io","slogan":"An Eye on your system","compose":"c2VydmljZXM6CiAgZ2xhbmNlczoKICAgIGltYWdlOiAnbmljb2xhcmdvL2dsYW5jZXM6bGF0ZXN0JwogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGVudmlyb25tZW50OgogICAgICAtIEdMQU5DRVNfT1BUPS13CiAgICAgIC0gU0VSVklDRV9GUUROX0dMQU5DRVNfNjEyMDgKICAgIHZvbHVtZXM6CiAgICAgIC0gJy92YXIvcnVuL2RvY2tlci5zb2NrOi92YXIvcnVuL2RvY2tlci5zb2NrOnJvJwogICAgICAtICcvcnVuL3VzZXIvMTAwMC9wb2RtYW4vcG9kbWFuLnNvY2s6L3J1bi91c2VyLzEwMDAvcG9kbWFuL3BvZG1hbi5zb2NrOnJvJwogICAgcGlkOiBob3N0CiAgICBwcml2aWxlZ2VkOiB0cnVlCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6NjEyMDgnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMzAK","tags":["monitoring tool python cross platform"],"logo":"svgs\/glances.png","minversion":"0.0.0","port":"61208"},"glitchtip":{"documentation":"https:\/\/glitchtip.com?utm_source=coolify.io","slogan":"GlitchTip is a self-hosted, open-source error tracking system.","compose":"c2VydmljZXM6CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZ2xpdGNodGlwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BnLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHJlZGlzOgogICAgaW1hZ2U6IHJlZGlzCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICB3ZWI6CiAgICBpbWFnZTogZ2xpdGNodGlwL2dsaXRjaHRpcAogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3JlcwogICAgICAtIHJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fR0xJVENIVElQXzgwODAKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUxAcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWdsaXRjaHRpcH0nCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfRU5DUllQVElPTgogICAgICAtICdFTUFJTF9VUkw9JHtFTUFJTF9VUkw6LWNvbnNvbGVtYWlsOi8vfScKICAgICAgLSAnR0xJVENIVElQX0RPTUFJTj0ke1NFUlZJQ0VfRlFETl9HTElUQ0hUSVB9JwogICAgICAtICdERUZBVUxUX0ZST01fRU1BSUw9JHtERUZBVUxUX0ZST01fRU1BSUw6LXRlc3RAZXhhbXBsZS5jb219JwogICAgICAtICdDRUxFUllfV09SS0VSX0FVVE9TQ0FMRT0ke0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFOi0xLDN9JwogICAgICAtICdDRUxFUllfV09SS0VSX01BWF9UQVNLU19QRVJfQ0hJTEQ9JHtDRUxFUllfV09SS0VSX01BWF9UQVNLU19QRVJfQ0hJTEQ6LTEwMDAwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3VwbG9hZHM6L2NvZGUvdXBsb2FkcycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBlY2hvCiAgICAgICAgLSBvawogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgd29ya2VyOgogICAgaW1hZ2U6IGdsaXRjaHRpcC9nbGl0Y2h0aXAKICAgIGNvbW1hbmQ6IC4vYmluL3J1bi1jZWxlcnktd2l0aC1iZWF0LnNoCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzCiAgICAgIC0gcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTUUw6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTEBwb3N0Z3Jlczo1NDMyLyR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZ2xpdGNodGlwfScKICAgICAgLSBTRUNSRVRfS0VZPSRTRVJWSUNFX0JBU0U2NF82NF9FTkNSWVBUSU9OCiAgICAgIC0gJ0VNQUlMX1VSTD0ke0VNQUlMX1VSTDotY29uc29sZW1haWw6Ly99JwogICAgICAtICdHTElUQ0hUSVBfRE9NQUlOPSR7U0VSVklDRV9GUUROX0dMSVRDSFRJUH0nCiAgICAgIC0gJ0RFRkFVTFRfRlJPTV9FTUFJTD0ke0RFRkFVTFRfRlJPTV9FTUFJTDotdGVzdEBleGFtcGxlLmNvbX0nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFPSR7Q0VMRVJZX1dPUktFUl9BVVRPU0NBTEU6LTEsM30nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRD0ke0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRDotMTAwMDB9JwogICAgdm9sdW1lczoKICAgICAgLSAndXBsb2FkczovY29kZS91cGxvYWRzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGVjaG8KICAgICAgICAtIG9rCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICBtaWdyYXRlOgogICAgaW1hZ2U6IGdsaXRjaHRpcC9nbGl0Y2h0aXAKICAgIHJlc3RhcnQ6ICdubycKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcG9zdGdyZXMKICAgICAgLSByZWRpcwogICAgY29tbWFuZDogJy4vbWFuYWdlLnB5IG1pZ3JhdGUnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUxAcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWdsaXRjaHRpcH0nCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfRU5DUllQVElPTgogICAgICAtICdFTUFJTF9VUkw9JHtFTUFJTF9VUkw6LWNvbnNvbGVtYWlsOi8vfScKICAgICAgLSAnREVGQVVMVF9GUk9NX0VNQUlMPSR7REVGQVVMVF9GUk9NX0VNQUlMOi10ZXN0QGV4YW1wbGUuY29tfScKICAgICAgLSAnQ0VMRVJZX1dPUktFUl9BVVRPU0NBTEU9JHtDRUxFUllfV09SS0VSX0FVVE9TQ0FMRTotMSwzfScKICAgICAgLSAnQ0VMRVJZX1dPUktFUl9NQVhfVEFTS1NfUEVSX0NISUxEPSR7Q0VMRVJZX1dPUktFUl9NQVhfVEFTS1NfUEVSX0NISUxEOi0xMDAwMH0nCg==","tags":["error","tracking","open-source","self-hosted","sentry"],"logo":"svgs\/glitchtip.png","minversion":"0.0.0","port":"8080"},"grafana-with-postgresql":{"documentation":"https:\/\/grafana.com?utm_source=coolify.io","slogan":"Grafana is the open source analytics & monitoring solution for every database.","compose":"c2VydmljZXM6CiAgZ3JhZmFuYToKICAgIGltYWdlOiBncmFmYW5hL2dyYWZhbmEtb3NzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fR1JBRkFOQV8zMDAwCiAgICAgIC0gJ0dGX1NFUlZFUl9ST09UX1VSTD0ke1NFUlZJQ0VfRlFETl9HUkFGQU5BfScKICAgICAgLSAnR0ZfU0VSVkVSX0RPTUFJTj0ke1NFUlZJQ0VfRlFETl9HUkFGQU5BfScKICAgICAgLSAnR0ZfU0VDVVJJVFlfQURNSU5fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX0dSQUZBTkF9JwogICAgICAtIEdGX0RBVEFCQVNFX1RZUEU9cG9zdGdyZXMKICAgICAgLSBHRl9EQVRBQkFTRV9IT1NUPXBvc3RncmVzcWwKICAgICAgLSBHRl9EQVRBQkFTRV9VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBHRl9EQVRBQkFTRV9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdHRl9EQVRBQkFTRV9OQU1FPSR7UE9TVEdSRVNfREI6LWdyYWZhbmF9JwogICAgdm9sdW1lczoKICAgICAgLSAnZ3JhZmFuYS1kYXRhOi92YXIvbGliL2dyYWZhbmEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMC9hcGkvaGVhbHRoJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzcWwKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotZ3JhZmFuYX0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["grafana","analytics","monitoring","dashboard"],"logo":"svgs\/grafana.svg","minversion":"0.0.0","port":"3000"},"grafana":{"documentation":"https:\/\/grafana.com?utm_source=coolify.io","slogan":"Grafana is the open source analytics & monitoring solution for every database.","compose":"c2VydmljZXM6CiAgZ3JhZmFuYToKICAgIGltYWdlOiBncmFmYW5hL2dyYWZhbmEtb3NzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fR1JBRkFOQV8zMDAwCiAgICAgIC0gJ0dGX1NFUlZFUl9ST09UX1VSTD0ke1NFUlZJQ0VfRlFETl9HUkFGQU5BfScKICAgICAgLSAnR0ZfU0VSVkVSX0RPTUFJTj0ke1NFUlZJQ0VfRlFETl9HUkFGQU5BfScKICAgICAgLSAnR0ZfU0VDVVJJVFlfQURNSU5fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX0dSQUZBTkF9JwogICAgdm9sdW1lczoKICAgICAgLSAnZ3JhZmFuYS1kYXRhOi92YXIvbGliL2dyYWZhbmEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMC9hcGkvaGVhbHRoJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["grafana","analytics","monitoring","dashboard"],"logo":"svgs\/grafana.svg","minversion":"0.0.0","port":"3000"},"grocy":{"documentation":"https:\/\/github.com\/grocy\/grocy?utm_source=coolify.io","slogan":"Grocy is a web-based household management and grocery list application.","compose":"c2VydmljZXM6CiAgZ3JvY3k6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvZ3JvY3k6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0dST0NZCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZ3JvY3ktY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["groceries","household","management","grocery","shopping"],"logo":"svgs\/grocy.svg","minversion":"0.0.0"},"heimdall":{"documentation":"https:\/\/github.com\/linuxserver\/Heimdall?utm_source=coolify.io","slogan":"Heimdall is a dashboard for managing and organizing your server applications.","compose":"c2VydmljZXM6CiAgaGVpbWRhbGw6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvaGVpbWRhbGw6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0hFSU1EQUxMCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnaGVpbWRhbGwtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["dashboard","server","applications","interface"],"logo":"svgs\/unknown.svg","minversion":"0.0.0"},"homepage":{"documentation":"https:\/\/gethomepage.dev\/latest\/?utm_source=coolify.io","slogan":"A modern, fully static, fast, secure fully proxied, highly customizable application dashboard","compose":"c2VydmljZXM6CiAgaG9tZXBhZ2U6CiAgICBpbWFnZTogJ2doY3IuaW8vZ2V0aG9tZXBhZ2UvaG9tZXBhZ2U6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0hPTUVQQUdFXzMwMDAKICAgICAgLSAnSE9NRVBBR0VfVkFSX0JBU0U9JHtTRVJWSUNFX0ZRRE5fSE9NRVBBR0V9JwogICAgdm9sdW1lczoKICAgICAgLSAnaG9tZXBhZ2UtY29uZmlnOi9hcHAvY29uZmlnJwogICAgICAtICdob21lcGFnZS1pbWFnZXM6L2FwcC9wdWJsaWMvaW1hZ2VzJwogICAgICAtICcvdmFyL3J1bi9kb2NrZXIuc29jazovdmFyL3J1bi9kb2NrZXIuc29jaycK","tags":["dashboard","homepage"],"logo":"svgs\/homepage.png","minversion":"0.0.0","port":"3000"},"jellyfin":{"documentation":"https:\/\/jellyfin.org?utm_source=coolify.io","slogan":"Jellyfin is a media server for hosting and streaming your media collection.","compose":"c2VydmljZXM6CiAgamVsbHlmaW46CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvamVsbHlmaW46bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0pFTExZRklOXzgwOTYKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICAgIC0gSkVMTFlGSU5fUHVibGlzaGVkU2VydmVyVXJsPSRTRVJWSUNFX0ZRRE5fSkVMTFlGSU4KICAgIHZvbHVtZXM6CiAgICAgIC0gJ2plbGx5ZmluLWNvbmZpZzovY29uZmlnJwogICAgICAtICdqZWxseWZpbi10dnNob3dzOi9kYXRhL3R2c2hvd3MnCiAgICAgIC0gJ2plbGx5ZmluLW1vdmllczovZGF0YS9tb3ZpZXMnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODA5NicKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=","tags":["media","server","movies","tv","music"],"logo":"svgs\/jellyfin.svg","minversion":"0.0.0","port":"8096"},"kuzzle":{"documentation":"https:\/\/kuzzle.io?utm_source=coolify.io","slogan":"Kuzzle is a generic backend offering the basic building blocks common to every application.","compose":"c2VydmljZXM6CiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjctYWxwaW5lJwogICAgY29tbWFuZDogJ3JlZGlzLXNlcnZlciAtLWFwcGVuZG9ubHkgeWVzJwogICAgdm9sdW1lczoKICAgICAgLSAnZWxhc3RpYy1yZWRpcy1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgZWxhc3RpY3NlYXJjaDoKICAgIGltYWdlOiAna3V6emxlaW8vZWxhc3RpY3NlYXJjaDo3JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjkyMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAycwogICAgICByZXRyaWVzOiAxMAogICAgdWxpbWl0czoKICAgICAgbm9maWxlOiA2NTUzNgogIGt1enpsZToKICAgIGltYWdlOiAna3V6emxlaW8va3V6emxlOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9LVVpaTEVfNzUxMgogICAgICAtICdrdXp6bGVfc2VydmljZXNfX3N0b3JhZ2VFbmdpbmVfX2NsaWVudF9fbm9kZT1odHRwOi8vZWxhc3RpY3NlYXJjaDo5MjAwJwogICAgICAtIGt1enpsZV9zZXJ2aWNlc19fc3RvcmFnZUVuZ2luZV9fY29tbW9uTWFwcGluZ19fZHluYW1pYz10cnVlCiAgICAgIC0ga3V6emxlX3NlcnZpY2VzX19pbnRlcm5hbENhY2hlX19ub2RlX19ob3N0PXJlZGlzCiAgICAgIC0ga3V6emxlX3NlcnZpY2VzX19tZW1vcnlTdG9yYWdlX19ub2RlX19ob3N0PXJlZGlzCiAgICAgIC0ga3V6emxlX3NlcnZlcl9fcHJvdG9jb2xzX19tcXR0X19lbmFibGVkPXRydWUKICAgICAgLSBrdXp6bGVfc2VydmVyX19wcm90b2NvbHNfX21xdHRfX2RldmVsb3BtZW50TW9kZT1mYWxzZQogICAgICAtIGt1enpsZV9saW1pdHNfX2xvZ2luc1BlclNlY29uZD01MAogICAgICAtIE5PREVfRU5WPXByb2R1Y3Rpb24KICAgICAgLSAnREVCVUc9JHtERUJVRzota3V6emxlOmNsdXN0ZXI6c3luY30nCiAgICAgIC0gJ0RFQlVHX0RFUFRIPSR7REVCVUdfREVQVEg6LTB9JwogICAgICAtICdERUJVR19NQVhfQVJSQVlfTEVOR1RIPSR7REVCVUdfTUFYX0FSUkFZOi0xMDB9JwogICAgICAtICdERUJVR19FWFBBTkQ9JHtERUJVR19FWFBBTkQ6LW9mZn0nCiAgICAgIC0gJ0RFQlVHX1NIT1dfSElEREVOPXskREVCVUdfU0hPV19ISURERU46LW9ufScKICAgICAgLSAnREVCVUdfQ09MT1JTPSR7REVCVUdfQ09MT1JTOi1vbn0nCiAgICBjYXBfYWRkOgogICAgICAtIFNZU19QVFJBQ0UKICAgIHVsaW1pdHM6CiAgICAgIG5vZmlsZTogNjU1MzYKICAgIHN5c2N0bHM6CiAgICAgIC0gbmV0LmNvcmUuc29tYXhjb25uPTgxOTIKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo3NTEyL19oZWFsdGhjaGVjaycKICAgICAgdGltZW91dDogMXMKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHJldHJpZXM6IDMwCiAgICBkZXBlbmRzX29uOgogICAgICByZWRpczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBlbGFzdGljc2VhcmNoOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5Cg==","tags":["backend","api","realtime","websocket","mqtt","rest","sdk","iot","geofencing","low-code"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"7512"},"listmonk":{"documentation":"https:\/\/listmonk.app\/?utm_source=coolify.io","slogan":"Self-hosted newsletter and mailing list manager","compose":"c2VydmljZXM6CiAgbGlzdG1vbms6CiAgICBpbWFnZTogJ2xpc3Rtb25rL2xpc3Rtb25rOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9MSVNUTU9OS185MDAwCiAgICAgIC0gJ0xJU1RNT05LX2FwcF9fYWRkcmVzcz0wLjAuMC4wOjkwMDAnCiAgICAgIC0gTElTVE1PTktfZGJfX2hvc3Q9cG9zdGdyZXMKICAgICAgLSBMSVNUTU9OS19kYl9fbmFtZT1saXN0bW9uawogICAgICAtIExJU1RNT05LX2RiX191c2VyPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBMSVNUTU9OS19kYl9fcGFzc3dvcmQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSBMSVNUTU9OS19kYl9fcG9ydD01NDMyCiAgICAgIC0gTElTVE1PTktfYXBwX19hZG1pbl91c2VybmFtZT1hZG1pbgogICAgICAtIExJU1RNT05LX2FwcF9fYWRtaW5fcGFzc3dvcmQ9JFNFUlZJQ0VfUEFTU1dPUkRfQURNSU4KICAgICAgLSBUWj1FdGMvVVRDCiAgICB2b2x1bWVzOgogICAgICAtICdsaXN0bW9uay1kYXRhOi9saXN0bW9uay91cGxvYWRzJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLXEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo5MDAwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgbGlzdG1vbmstaW5pdGlhbC1kYXRhYmFzZS1zZXR1cDoKICAgIGltYWdlOiAnbGlzdG1vbmsvbGlzdG1vbms6bGF0ZXN0JwogICAgY29tbWFuZDogJy4vbGlzdG1vbmsgLS1pbnN0YWxsIC0teWVzIC0taWRlbXBvdGVudCcKICAgIHJlc3RhcnQ6ICdubycKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBMSVNUTU9OS19kYl9faG9zdD1wb3N0Z3JlcwogICAgICAtIExJU1RNT05LX2RiX19uYW1lPWxpc3Rtb25rCiAgICAgIC0gTElTVE1PTktfZGJfX3VzZXI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIExJU1RNT05LX2RiX19wYXNzd29yZD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtIExJU1RNT05LX2RiX19wb3J0PTU0MzIKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfREI9bGlzdG1vbmsKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgdm9sdW1lczoKICAgICAgLSAncGctZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["newsletter","mailing list","self-hosted","open source"],"logo":"svgs\/listmonk.svg","minversion":"0.0.0","port":"9000"},"logto":{"documentation":"https:\/\/docs.logto.io\/docs\/tutorials\/get-started\/#logto-oss-self-hosted?utm_source=coolify.io","slogan":"A comprehensive identity solution covering both the front and backend, complete with pre-built infrastructure and enterprise-grade solutions.","compose":"c2VydmljZXM6CiAgbG9ndG86CiAgICBpbWFnZTogJ3N2aGQvbG9ndG86JHtUQUctbGF0ZXN0fScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnRyeXBvaW50OgogICAgICAtIHNoCiAgICAgIC0gJy1jJwogICAgICAtICducG0gcnVuIGNsaSBkYiBzZWVkIC0tIC0tc3dlICYmIG5wbSBzdGFydCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFRSVVNUX1BST1hZX0hFQURFUj0xCiAgICAgIC0gJ0RCX1VSTD1wb3N0Z3JlczovLyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfToke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9QHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU19EQjotbG9ndG99JwogICAgICAtIEVORFBPSU5UPSRMT0dUT19FTkRQT0lOVAogICAgICAtIEFETUlOX0VORFBPSU5UPSRMT0dUT19BRE1JTl9FTkRQT0lOVAogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdleGl0IDAnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6MTQtYWxwaW5lJwogICAgdXNlcjogcG9zdGdyZXMKICAgIGVudmlyb25tZW50OgogICAgICBQT1NUR1JFU19VU0VSOiAnJHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICBQT1NUR1JFU19QQVNTV09SRDogJyR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIFBPU1RHUkVTX0RCOiAnJHtQT1NUR1JFU19EQjotbG9ndG99JwogICAgdm9sdW1lczoKICAgICAgLSAnbG9ndG8tcG9zdGdyZXMtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcGdfaXNyZWFkeQogICAgICAgIC0gJy1VJwogICAgICAgIC0gJFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAgIC0gJy1kJwogICAgICAgIC0gJFBPU1RHUkVTX0RCCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["logto","identity","login","authentication","oauth","oidc","openid"],"logo":"svgs\/logto_dark.svg","minversion":"0.0.0"},"mediawiki":{"documentation":"https:\/\/www.mediawiki.org?utm_source=coolify.io","slogan":"MediaWiki is a collaboration and documentation platform brought to you by a vibrant community.","compose":"c2VydmljZXM6CiAgbWVkaWF3aWtpOgogICAgaW1hZ2U6ICdtZWRpYXdpa2k6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX01FRElBV0lLSV84MAogICAgdm9sdW1lczoKICAgICAgLSAnbWVkaWF3aWtpLWltYWdlczovdmFyL3d3dy9odG1sL2ltYWdlcycKICAgICAgLSAnbWVkaWF3aWtpLXNxbGl0ZTovdmFyL3d3dy9odG1sL2RhdGEnCiAgICAgIC0gJy4vTG9jYWxTZXR0aW5ncy5waHA6L3Zhci93d3cvaHRtbC9Mb2NhbFNldHRpbmdzLnBocCcKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo4MCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["wiki","collaboration","documentation"],"logo":"svgs\/mediawiki.ico","minversion":"0.0.0","port":"80"},"meilisearch":{"documentation":"https:\/\/www.meilisearch.com?utm_source=coolify.io","slogan":"MeiliSearch is a powerful, fast, easy to use and deploy search engine.","compose":"c2VydmljZXM6CiAgbWVpbGlzZWFyY2g6CiAgICBpbWFnZTogJ2dldG1laWxpL21laWxpc2VhcmNoOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NRUlMSVNFQVJDSF83NzAwCiAgICAgIC0gJ01FSUxJX05PX0FOQUxZVElDUz0ke01FSUxJX05PX0FOQUxZVElDUzotdHJ1ZX0nCiAgICAgIC0gJ01FSUxJX0VOVj0ke01FSUxJX0VOVjotcHJvZHVjdGlvbn0nCiAgICAgIC0gJ01FSUxJX01BU1RFUl9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEX01FSUxJU0VBUkNIfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ21laWxpc2VhcmNoLWRhdGE6L21laWxpX2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6NzcwMC9oZWFsdGgnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["search","engine","fulltext","full","text","meilisearch"],"logo":"svgs\/meilisearch.svg","minversion":"0.0.0","port":"7700"},"metabase":{"documentation":"https:\/\/www.metabase.com?utm_source=coolify.io","slogan":"Fast analytics with the friendly UX and integrated tooling to let your company explore data on their own.","compose":"c2VydmljZXM6CiAgbWV0YWJhc2U6CiAgICBpbWFnZTogJ21ldGFiYXNlL21ldGFiYXNlOmxhdGVzdCcKICAgIHZvbHVtZXM6CiAgICAgIC0gJy9kZXYvdXJhbmRvbTovZGV2L3JhbmRvbTpybycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NRVRBQkFTRV8zMDAwCiAgICAgIC0gTUJfREJfVFlQRT1wb3N0Z3JlcwogICAgICAtIE1CX0RCX0hPU1Q9cG9zdGdyZXNxbAogICAgICAtIE1CX0RCX1BPUlQ9NTQzMgogICAgICAtICdNQl9EQl9EQk5BTUU9JHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1tZXRhYmFzZX0nCiAgICAgIC0gTUJfREJfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTUUwKICAgICAgLSBNQl9EQl9QQVNTPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUwKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAnY3VybCAtLWZhaWwgLUkgaHR0cDovLzEyNy4wLjAuMTozMDAwL2FwaS9oZWFsdGggfHwgZXhpdCAxJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAnbWV0YWJhc2UtcG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotbWV0YWJhc2V9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["analytics","bi","business","intelligence"],"logo":"svgs\/metabase.svg","minversion":"0.0.0","port":"3000"},"metube":{"documentation":"https:\/\/github.com\/alexta69\/metube?utm_source=coolify.io","slogan":"A web GUI for youtube-dl with playlist support. It enables you to effortlessly download videos from YouTube and dozens of other sites.","compose":"c2VydmljZXM6CiAgbWV0dWJlOgogICAgaW1hZ2U6ICdnaGNyLmlvL2FsZXh0YTY5L21ldHViZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTUVUVUJFXzgwODEKICAgICAgLSBVSUQ9MTAwMAogICAgICAtIEdJRD0xMDAwCiAgICB2b2x1bWVzOgogICAgICAtICdtZXR1YmUtZG93bmxvYWRzOi9kb3dubG9hZHMnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODA4MScKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=","tags":["youtube","download","videos","playlist"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"8081"},"minio":{"documentation":"https:\/\/min.io\/docs\/minio\/container\/index.html?utm_source=coolify.io","slogan":"MinIO is a high performance object storage server compatible with Amazon S3 APIs.","compose":"c2VydmljZXM6CiAgbWluaW86CiAgICBpbWFnZTogJ3F1YXkuaW8vbWluaW8vbWluaW86bGF0ZXN0JwogICAgY29tbWFuZDogJ3NlcnZlciAvZGF0YSAtLWNvbnNvbGUtYWRkcmVzcyAiOjkwMDEiJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTUlOSU9fU0VSVkVSX1VSTD0kTUlOSU9fU0VSVkVSX1VSTAogICAgICAtIE1JTklPX0JST1dTRVJfUkVESVJFQ1RfVVJMPSRNSU5JT19CUk9XU0VSX1JFRElSRUNUX1VSTAogICAgICAtIE1JTklPX1JPT1RfVVNFUj0kU0VSVklDRV9VU0VSX01JTklPCiAgICAgIC0gTUlOSU9fUk9PVF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9NSU5JTwogICAgdm9sdW1lczoKICAgICAgLSAnbWluaW8tZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo5MDAwL21pbmlvL2hlYWx0aC9saXZlJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["object","storage","server","s3","api"],"logo":"svgs\/minio.svg","minversion":"0.0.0"},"moodle":{"documentation":"https:\/\/moodle.org?utm_source=coolify.io","slogan":"Moodle is the world\u2019s most customisable and trusted eLearning solution that empowers educators to improve our world.","compose":"c2VydmljZXM6CiAgbWFyaWFkYjoKICAgIGltYWdlOiAnbWFyaWFkYjoxMS4xJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gQUxMT1dfRU1QVFlfUEFTU1dPUkQ9bm8KICAgICAgLSBNWVNRTF9ST09UX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1JPT1QKICAgICAgLSBNWVNRTF9EQVRBQkFTRT1iaXRuYW1pX21vb2RsZQogICAgICAtIE1ZU1FMX1VTRVI9JFNFUlZJQ0VfVVNFUl9NQVJJQURCCiAgICAgIC0gTVlTUUxfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfTUFSSUFEQgogICAgICAtIE1BUklBREJfQ0hBUkFDVEVSX1NFVD11dGY4bWI0CiAgICAgIC0gTUFSSUFEQl9DT0xMQVRFPXV0ZjhtYjRfdW5pY29kZV9jaQogICAgdm9sdW1lczoKICAgICAgLSAnbWFyaWFkYi1kYXRhOi92YXIvbGliL215c3FsJwogIG1vb2RsZToKICAgIGltYWdlOiAnZG9ja2VyLmlvL2JpdG5hbWkvbW9vZGxlOjQuMycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NT09ETEVfODA4MAogICAgICAtIE1PT0RMRV9EQVRBQkFTRV9IT1NUPW1hcmlhZGIKICAgICAgLSBNT09ETEVfREFUQUJBU0VfUE9SVF9OVU1CRVI9MzMwNgogICAgICAtIE1PT0RMRV9EQVRBQkFTRV9VU0VSPSRTRVJWSUNFX1VTRVJfTUFSSUFEQgogICAgICAtIE1PT0RMRV9EQVRBQkFTRV9OQU1FPWJpdG5hbWlfbW9vZGxlCiAgICAgIC0gTU9PRExFX0RBVEFCQVNFX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX01BUklBREIKICAgICAgLSBBTExPV19FTVBUWV9QQVNTV09SRD1ubwogICAgICAtICdNT09ETEVfVVNFUk5BTUU9JHtNT09ETEVfVVNFUk5BTUU6LXVzZXJ9JwogICAgICAtIE1PT0RMRV9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9NT09ETEUKICAgICAgLSBNT09ETEVfRU1BSUw9dXNlckBleGFtcGxlLmNvbQogICAgICAtICdNT09ETEVfU0lURV9OQU1FPSR7TU9PRExFX1NJVEVfTkFNRTotTmV3IFNpdGV9JwogICAgdm9sdW1lczoKICAgICAgLSAnbW9vZGxlLWRhdGE6L2JpdG5hbWkvbW9vZGxlJwogICAgICAtICdtb29kbGVkYXRhLWRhdGE6L2JpdG5hbWkvbW9vZGxlZGF0YScKICAgIGRlcGVuZHNfb246CiAgICAgIC0gbWFyaWFkYgo=","tags":["moodle","elearning","education","lms","cms","open","source","low","code"],"logo":"svgs\/moodle.png","minversion":"0.0.0","port":"8080"},"n8n-with-postgresql":{"documentation":"https:\/\/n8n.io?utm_source=coolify.io","slogan":"n8n is an extendable workflow automation tool.","compose":"c2VydmljZXM6CiAgbjhuOgogICAgaW1hZ2U6IGRvY2tlci5uOG4uaW8vbjhuaW8vbjhuCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTjhOXzU2NzgKICAgICAgLSAnTjhOX0VESVRPUl9CQVNFX1VSTD0ke1NFUlZJQ0VfRlFETl9OOE59JwogICAgICAtICdXRUJIT09LX1VSTD0ke1NFUlZJQ0VfRlFETl9OOE59JwogICAgICAtICdOOE5fSE9TVD0ke1NFUlZJQ0VfVVJMX044Tn0nCiAgICAgIC0gR0VORVJJQ19USU1FWk9ORT1FdXJvcGUvQmVybGluCiAgICAgIC0gVFo9RXVyb3BlL0JlcmxpbgogICAgICAtIERCX1RZUEU9cG9zdGdyZXNkYgogICAgICAtICdEQl9QT1NUR1JFU0RCX0RBVEFCQVNFPSR7UE9TVEdSRVNfREI6LW44bn0nCiAgICAgIC0gREJfUE9TVEdSRVNEQl9IT1NUPXBvc3RncmVzcWwKICAgICAgLSBEQl9QT1NUR1JFU0RCX1BPUlQ9NTQzMgogICAgICAtIERCX1BPU1RHUkVTREJfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gREJfUE9TVEdSRVNEQl9TQ0hFTUE9cHVibGljCiAgICAgIC0gREJfUE9TVEdSRVNEQl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgdm9sdW1lczoKICAgICAgLSAnbjhuLWRhdGE6L2hvbWUvbm9kZS8ubjhuJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICd3Z2V0IC1xTy0gaHR0cDovLzEyNy4wLjAuMTo1Njc4LycKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1uOG59JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["n8n","workflow","automation","open","source","low","code"],"logo":"svgs\/n8n.png","minversion":"0.0.0","port":"5678"},"n8n":{"documentation":"https:\/\/n8n.io?utm_source=coolify.io","slogan":"n8n is an extendable workflow automation tool.","compose":"c2VydmljZXM6CiAgbjhuOgogICAgaW1hZ2U6IGRvY2tlci5uOG4uaW8vbjhuaW8vbjhuCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTjhOXzU2NzgKICAgICAgLSAnTjhOX0VESVRPUl9CQVNFX1VSTD0ke1NFUlZJQ0VfRlFETl9OOE59JwogICAgICAtICdXRUJIT09LX1VSTD0ke1NFUlZJQ0VfRlFETl9OOE59JwogICAgICAtICdOOE5fSE9TVD0ke1NFUlZJQ0VfVVJMX044Tn0nCiAgICAgIC0gR0VORVJJQ19USU1FWk9ORT1FdXJvcGUvQmVybGluCiAgICAgIC0gVFo9RXVyb3BlL0JlcmxpbgogICAgdm9sdW1lczoKICAgICAgLSAnbjhuLWRhdGE6L2hvbWUvbm9kZS8ubjhuJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICd3Z2V0IC1xTy0gaHR0cDovLzEyNy4wLjAuMTo1Njc4LycKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["n8n","workflow","automation","open","source","low","code"],"logo":"svgs\/n8n.png","minversion":"0.0.0","port":"5678"},"next-image-transformation":{"documentation":"https:\/\/github.com\/coollabsio\/next-image-transformation?utm_source=coolify.io","slogan":"Drop-in replacement for Vercel's Nextjs image optimization service.","compose":"c2VydmljZXM6CiAgbmV4dC1pbWFnZS10cmFuc2Zvcm1hdGlvbjoKICAgIGltYWdlOiAnZ2hjci5pby9jb29sbGFic2lvL25leHQtaW1hZ2UtdHJhbnNmb3JtYXRpb246bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1RSQU5TRk9STUFUSU9OXzMwMDAKICAgICAgLSBOT0RFX0VOVj1wcm9kdWN0aW9uCiAgICAgIC0gJ0FMTE9XRURfUkVNT1RFX0RPTUFJTlM9JHtBTExPV0VEX1JFTU9URV9ET01BSU5TOi0qfScKICAgICAgLSAnSU1HUFJPWFlfVVJMPSR7SU1HUFJPWFlfVVJMOi1odHRwOi8vaW1ncHJveHk6ODA4MH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogJ3dnZXQgLXFPLSBodHRwOi8vMTI3LjAuMC4xOjMwMDAvaGVhbHRoIHx8IGV4aXQgMScKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiA1CiAgaW1ncHJveHk6CiAgICBpbWFnZTogZGFydGhzaW0vaW1ncHJveHkKICAgIGVudmlyb25tZW50OgogICAgICAtIElNR1BST1hZX0VOQUJMRV9XRUJQX0RFVEVDVElPTj10cnVlCiAgICAgIC0gSU1HUFJPWFlfSlBFR19QUk9HUkVTU0lWRT10cnVlCiAgICAgIC0gSU1HUFJPWFlfVVNFX0VUQUc9dHJ1ZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGltZ3Byb3h5CiAgICAgICAgLSBoZWFsdGgKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiA1Cg==","tags":["nextjs","image","transformation","service"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"3000"},"nextcloud":{"documentation":"https:\/\/docs.nextcloud.com?utm_source=coolify.io","slogan":"NextCloud is a self-hosted, open-source platform that provides file storage, collaboration, and communication tools for seamless data management.","compose":"c2VydmljZXM6CiAgbmV4dGNsb3VkOgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL25leHRjbG91ZDpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTkVYVENMT1VECiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnbmV4dGNsb3VkLWNvbmZpZzovY29uZmlnJwogICAgICAtICduZXh0Y2xvdWQtZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=","tags":["cloud","collaboration","communication","filestorage","data"],"logo":"svgs\/nextcloud.svg","minversion":"0.0.0"},"nocodb":{"documentation":"https:\/\/nocodb.com\/?utm_source=coolify.io","slogan":"NocoDB is an open source Airtable alternative. Turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart-spreadsheet.","compose":"c2VydmljZXM6CiAgbm9jb2RiOgogICAgaW1hZ2U6IG5vY29kYi9ub2NvZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9OT0NPREJfODA4MAogICAgdm9sdW1lczoKICAgICAgLSAnbm9jb2RiLWRhdGE6L3Vzci9hcHAvZGF0YS8nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gd2dldAogICAgICAgIC0gJy1xJwogICAgICAgIC0gJy0tc3BpZGVyJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODA4MCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["nocodb","airtable","mysql","postgresql","sqlserver","sqlite","mariadb"],"logo":"svgs\/nocodb.svg","minversion":"0.0.0","port":"8080"},"odoo":{"documentation":"https:\/\/www.odoo.com\/?utm_source=coolify.io","slogan":"Odoo is a suite of open-source business apps that cover all your company needs.","compose":"c2VydmljZXM6CiAgb2RvbzoKICAgIGltYWdlOiAnb2RvbzoxNycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9PRE9PXzgwNjkKICAgICAgLSBIT1NUPXBvc3RncmVzcWwKICAgICAgLSBVU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBQQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgdm9sdW1lczoKICAgICAgLSAnb2Rvby13ZWItZGF0YTovdmFyL2xpYi9vZG9vJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwNjknCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMzAKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19EQj1wb3N0Z3JlcwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kIHBvc3RncmVzJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["business","apps","crm","ecommerce","accounting","inventory","point of sale","project management","open-source"],"logo":"svgs\/odoo.svg","minversion":"0.0.0","port":"8069"},"openblocks":{"documentation":"https:\/\/openblocks.dev?utm_source=coolify.io","slogan":"OpenBlocks is a self-hosted, open-source, low-code platform for building internal tools.","compose":"c2VydmljZXM6CiAgb3BlbmJsb2NrczoKICAgIGltYWdlOiBvcGVuYmxvY2tzZGV2L29wZW5ibG9ja3MtY2UKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9PUEVOQkxPQ0tTXzMwMDAKICAgICAgLSAnRU5BQkxFX1VTRVJfU0lHTl9VUD0ke0VOQUJMRV9VU0VSX1NJR05fVVA6LXRydWV9JwogICAgICAtIEVOQ1JZUFRJT05fUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfRU5DUllQVElPTgogICAgICAtIEVOQ1JZUFRJT05fU0FMVD0kU0VSVklDRV9QQVNTV09SRF9TQUxUCiAgICB2b2x1bWVzOgogICAgICAtICdvcGVuYmxvY2tzLWRhdGE6L29wZW5ibG9ja3Mtc3RhY2tzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjMwMDAvaGVhbHRoJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["openblocks","low","code","platform","open","source","low","code"],"logo":"svgs\/openblocks.svg","minversion":"0.0.0","port":"3000"},"pairdrop":{"documentation":"https:\/\/pairdrop.net\/?utm_source=coolify.io","slogan":"Pairdrop is a self-hosted file sharing and collaboration platform, offering secure file sharing and collaboration capabilities for efficient teamwork.","compose":"c2VydmljZXM6CiAgcGFpcmRyb3A6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvcGFpcmRyb3A6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1BBSVJEUk9QXzMwMDAKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICAgIC0gREVCVUdfTU9ERT1mYWxzZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjMwMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["file","sharing","collaboration","teamwork"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"3000"},"penpot":{"documentation":"https:\/\/help.penpot.app\/technical-guide\/getting-started\/#install-with-docker?utm_source=coolify.io","slogan":"Penpot is the first Open Source design and prototyping platform for product teams.","compose":"c2VydmljZXM6CiAgZnJvbnRlbmQ6CiAgICBpbWFnZTogJ3BlbnBvdGFwcC9mcm9udGVuZDpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdwZW5wb3QtYXNzZXRzOi9vcHQvZGF0YS9hc3NldHMnCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBlbnBvdC1iYWNrZW5kCiAgICAgIC0gcGVucG90LWV4cG9ydGVyCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fRlJPTlRFTkQKICAgICAgLSAnUEVOUE9UX0ZMQUdTPSR7UEVOUE9UX0ZST05URU5EX0ZMQUdTOi1lbmFibGUtbG9naW4td2l0aC1wYXNzd29yZH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICBwZW5wb3QtYmFja2VuZDoKICAgIGltYWdlOiAncGVucG90YXBwL2JhY2tlbmQ6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAncGVucG90LWFzc2V0czovb3B0L2RhdGEvYXNzZXRzJwogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3JlcwogICAgICAtIHJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUEVOUE9UX0ZMQUdTPSR7UEVOUE9UX0JBQ0tFTkRfRkxBR1M6LWVuYWJsZS1sb2dpbi13aXRoLXBhc3N3b3JkIGVuYWJsZS1zbXRwIGVuYWJsZS1wcmVwbC1zZXJ2ZXJ9JwogICAgICAtIFBFTlBPVF9IVFRQX1NFUlZFUl9QT1JUPTYwNjAKICAgICAgLSBQRU5QT1RfU0VDUkVUX0tFWT0kU0VSVklDRV9SRUFMQkFTRTY0XzY0X1BFTlBPVAogICAgICAtIFBFTlBPVF9QVUJMSUNfVVJJPSRTRVJWSUNFX0ZRRE5fRlJPTlRFTkQKICAgICAgLSAnUEVOUE9UX0JBQ0tFTkRfVVJJPWh0dHA6Ly9wZW5wb3QtYmFja2VuZCcKICAgICAgLSAnUEVOUE9UX0VYUE9SVEVSX1VSST1odHRwOi8vcGVucG90LWV4cG9ydGVyJwogICAgICAtICdQRU5QT1RfREFUQUJBU0VfVVJJPXBvc3RncmVzcWw6Ly9wb3N0Z3Jlcy8ke1BPU1RHUkVTX0RCOi1wZW5wb3R9JwogICAgICAtICdQRU5QT1RfREFUQUJBU0VfVVNFUk5BTUU9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQRU5QT1RfREFUQUJBU0VfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfScKICAgICAgLSAnUEVOUE9UX1JFRElTX1VSST1yZWRpczovL3JlZGlzLzAnCiAgICAgIC0gUEVOUE9UX0FTU0VUU19TVE9SQUdFX0JBQ0tFTkQ9YXNzZXRzLWZzCiAgICAgIC0gUEVOUE9UX1NUT1JBR0VfQVNTRVRTX0ZTX0RJUkVDVE9SWT0vb3B0L2RhdGEvYXNzZXRzCiAgICAgIC0gJ1BFTlBPVF9URUxFTUVUUllfRU5BQkxFRD0ke1BFTlBPVF9URUxFTUVUUllfRU5BQkxFRDotZmFsc2V9JwogICAgICAtICdQRU5QT1RfU01UUF9ERUZBVUxUX0ZST009JHtQRU5QT1RfU01UUF9ERUZBVUxUX0ZST006LW5vLXJlcGx5QGV4YW1wbGUuY29tfScKICAgICAgLSAnUEVOUE9UX1NNVFBfREVGQVVMVF9SRVBMWV9UTz0ke1BFTlBPVF9TTVRQX0RFRkFVTFRfUkVQTFlfVE86LW5vLXJlcGx5QGV4YW1wbGUuY29tfScKICAgICAgLSAnUEVOUE9UX1NNVFBfSE9TVD0ke1BFTlBPVF9TTVRQX0hPU1Q6LW1haWxwaXR9JwogICAgICAtICdQRU5QT1RfU01UUF9QT1JUPSR7UEVOUE9UX1NNVFBfUE9SVDotMTAyNX0nCiAgICAgIC0gJ1BFTlBPVF9TTVRQX1VTRVJOQU1FPSR7UEVOUE9UX1NNVFBfVVNFUk5BTUU6LXBlbnBvdH0nCiAgICAgIC0gJ1BFTlBPVF9TTVRQX1BBU1NXT1JEPSR7UEVOUE9UX1NNVFBfUEFTU1dPUkQ6LXBlbnBvdH0nCiAgICAgIC0gJ1BFTlBPVF9TTVRQX1RMUz0ke1BFTlBPVF9TTVRQX1RMUzotZmFsc2V9JwogICAgICAtICdQRU5QT1RfU01UUF9TU0w9JHtQRU5QT1RfU01UUF9TU0w6LWZhbHNlfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo2MDYwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgcGVucG90LWV4cG9ydGVyOgogICAgaW1hZ2U6ICdwZW5wb3RhcHAvZXhwb3J0ZXI6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUEVOUE9UX1BVQkxJQ19VUkk9JFNFUlZJQ0VfRlFETl9GUk9OVEVORAogICAgICAtICdQRU5QT1RfUkVESVNfVVJJPXJlZGlzOi8vcmVkaXMvMCcKICBtYWlscGl0OgogICAgaW1hZ2U6ICdheGxsZW50L21haWxwaXQ6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX01BSUxQSVRfODAyNQogIHBvc3RncmVzOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BlbnBvdC1wb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfSU5JVERCX0FSR1M9LS1kYXRhLWNoZWNrc3VtcwogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LXBlbnBvdH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjctYWxwaW5lJwogICAgY29tbWFuZDogJ3JlZGlzLXNlcnZlciAtLWFwcGVuZG9ubHkgeWVzJwogICAgdm9sdW1lczoKICAgICAgLSAncGVucG90LXJlZGlzLWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["penpot","design","prototyping","figma","open","source"],"logo":"svgs\/unknown.svg","minversion":"0.0.0"},"phpmyadmin":{"documentation":"https:\/\/phpmyadmin.net?utm_source=coolify.io","slogan":"phpMyAdmin is a web-based database management tool for administering your MySQL and MariaDB databases through a user-friendly interface.","compose":"c2VydmljZXM6CiAgcGhwbXlhZG1pbjoKICAgIGltYWdlOiAnbHNjci5pby9saW51eHNlcnZlci9waHBteWFkbWluOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9QSFBNWUFETUlOCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgICAtIFBNQV9BUkJJVFJBUlk9MQogICAgICAtIFBNQV9BQlNPTFVURV9VUkk9JFNFUlZJQ0VfRlFETl9QSFBNWUFETUlOCiAgICB2b2x1bWVzOgogICAgICAtICdwaHBteWFkbWluLWNvbmZpZzovY29uZmlnJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==","tags":["database management"],"logo":"svgs\/phpmyadmin.svg","minversion":"0.0.0"},"pocketbase":{"documentation":"https:\/\/pocketbase.io\/docs\/?utm_source=coolify.io","slogan":"Open Source backend for your next SaaS and Mobile app in 1 file","compose":"c2VydmljZXM6CiAgcG9ja2V0YmFzZToKICAgIGltYWdlOiAnZ2hjci5pby9jb29sbGFic2lvL3BvY2tldGJhc2U6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1BPQ0tFVEJBU0VfODA4MAogICAgdm9sdW1lczoKICAgICAgLSAncG9ja2V0YmFzZS1kYXRhOi9hcHAvcGJfZGF0YScKICAgICAgLSAncG9ja2V0YmFzZS1ob29rczovYXBwL3BiX2hvb2tzJwo=","tags":["pocketbase","backend","saas","mobile","api"],"logo":"svgs\/pocketbase.svg","minversion":"0.0.0","port":"8080"},"posthog":{"documentation":"https:\/\/posthog.com?utm_source=coolify.io","slogan":"The single platform to analyze, test, observe, and deploy new features","compose":"c2VydmljZXM6CiAgZGI6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjEyLWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3Rob2ctcG9zdGdyZXMtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19VU0VSPXBvc3Rob2cKICAgICAgLSBQT1NUR1JFU19EQj1wb3N0aG9nCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSBwb3N0aG9nJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjYuMi43LWFscGluZScKICAgIGNvbW1hbmQ6ICdyZWRpcy1zZXJ2ZXIgLS1tYXhtZW1vcnktcG9saWN5IGFsbGtleXMtbHJ1IC0tbWF4bWVtb3J5IDIwMG1iJwogIGNsaWNraG91c2U6CiAgICBpbWFnZTogJ2NsaWNraG91c2UvY2xpY2tob3VzZS1zZXJ2ZXI6MjMuMTEuMi4xMS1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9pZGwvZXZlbnRzX2RlYWRfbGV0dGVyX3F1ZXVlLmpzb24KICAgICAgICB0YXJnZXQ6IC9pZGwvZXZlbnRzX2RlYWRfbGV0dGVyX3F1ZXVlLmpzb24KICAgICAgICBjb250ZW50OiAie1xuICBcIiRzY2hlbWFcIjogXCJodHRwczovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC8yMDIwLTEyL3NjaGVtYVwiLFxuICBcIiRpZFwiOiBcImZpbGU6Ly9wb3N0aG9nL2lkbC9ldmVudHNfZGVhZF9sZXR0ZXJfcXVldWUuanNvblwiLFxuICBcInRpdGxlXCI6IFwiZXZlbnRzX2RlYWRfbGV0dGVyX3F1ZXVlXCIsXG4gIFwiZGVzY3JpcHRpb25cIjogXCJFdmVudHMgdGhhdCBmYWlsZWQgdG8gYmUgdmFsaWRhdGVkIG9yIHByb2Nlc3NlZCBhbmQgYXJlIHNlbnQgdG8gdGhlIERMUVwiLFxuICBcInR5cGVcIjogXCJvYmplY3RcIixcbiAgXCJwcm9wZXJ0aWVzXCI6IHtcbiAgICAgIFwiaWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJ1dWlkIGZvciB0aGUgc3VibWlzc2lvblwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICB9LFxuICAgICAgXCJldmVudF91dWlkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwidXVpZCBmb3IgdGhlIGV2ZW50XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImV2ZW50XCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiZXZlbnQgdHlwZVwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICB9LFxuICAgICAgXCJwcm9wZXJ0aWVzXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBwcm9wZXJ0aWVzIGpzb24gb2JqZWN0XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImRpc3RpbmN0X2lkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiUG9zdEhvZyBkaXN0aW5jdF9pZFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICB9LFxuICAgICAgXCJ0ZWFtX2lkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwidGVhbV9pZCAobWFwcyB0byB0aGUgcHJvamVjdCB1bmRlciB0aGUgb3JnYW5pemF0aW9uKVwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJlbGVtZW50c19jaGFpblwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlVzZWQgZm9yIGF1dG9jYXB0dXJlLiBET00gZWxlbWVudCBoaWVyYXJjaHlcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgfSxcbiAgICAgIFwiY3JlYXRlZF9hdFwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlVzZWQgZm9yIGF1dG9jYXB0dXJlLiBET00gZWxlbWVudCBoaWVyYXJjaHlcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgfSxcbiAgICAgIFwiaXBcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJJUCBBZGRyZXNzIG9mIHRoZSBhc3NvY2lhdGVkIHdpdGggdGhlIGV2ZW50XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcInNpdGVfdXJsXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU2l0ZSBVUkwgYXNzb2NpYXRlZCB3aXRoIHRoZSBldmVudCB0aGUgZXZlbnRcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgfSxcbiAgICAgIFwibm93XCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiVGltZXN0YW1wIG9mIHRoZSBETFEgZXZlbnRcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgfSxcbiAgICAgIFwicmF3X3BheWxvYWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJSYXcgcGF5bG9hZCBvZiB0aGUgZXZlbnQgdGhhdCBmYWlsZWQgdG8gYmUgY29uc3VtZWRcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgfSxcbiAgICAgIFwiZXJyb3JfdGltZXN0YW1wXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiVGltZXN0YW1wIHRoYXQgdGhlIGVycm9yIG9mIGluZ2VzdGlvbiBvY2N1cnJlZFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJlcnJvcl9sb2NhdGlvblwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlNvdXJjZSBvZiBlcnJvciBpZiBrbm93blwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICB9LFxuICAgICAgXCJlcnJvclwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIkVycm9yIGlmIGtub3duXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcInRhZ3NcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJUYWdzIGFzc29jaWF0ZWQgd2l0aCB0aGUgZXJyb3Igb3IgZXZlbnRcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJhcnJheVwiLFxuICAgICAgICAgIFwiaXRlbXNcIjoge1xuICAgICAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgICAgIH1cbiAgICAgIH1cbiAgfSxcbiAgXCJyZXF1aXJlZFwiOiBbXCJyYXdfcGF5bG9hZFwiXVxufVxuIgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9pZGwvZXZlbnRzX2pzb24uanNvbgogICAgICAgIHRhcmdldDogL2lkbC9ldmVudHNfanNvbi5qc29uCiAgICAgICAgY29udGVudDogIntcbiAgXCIkc2NoZW1hXCI6IFwiaHR0cHM6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQvMjAyMC0xMi9zY2hlbWFcIixcbiAgXCIkaWRcIjogXCJmaWxlOi8vcG9zdGhvZy9pZGwvZXZlbnRzX2pzb24uanNvblwiLFxuICBcInRpdGxlXCI6IFwiZXZlbnRzX2pzb25cIixcbiAgXCJkZXNjcmlwdGlvblwiOiBcIkV2ZW50IHNjaGVtYSB0aGF0IGlzIGRlc3RpbmVkIGZvciBDbGlja0hvdXNlXCIsXG4gIFwidHlwZVwiOiBcIm9iamVjdFwiLFxuICBcInByb3BlcnRpZXNcIjoge1xuICAgICAgXCJ1dWlkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwidXVpZCBmb3IgdGhlIGV2ZW50XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImV2ZW50XCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiZXZlbnQgdHlwZVwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICB9LFxuICAgICAgXCJwcm9wZXJ0aWVzXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBwcm9wZXJ0aWVzIGpzb24gb2JqZWN0XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcInRpbWVzdGFtcFwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlRpbWVzdGFtcCB0aGF0IHRoZSBldmVudCBvY2N1cnJlZFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJ0ZWFtX2lkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwidGVhbV9pZCAobWFwcyB0byB0aGUgcHJvamVjdCB1bmRlciB0aGUgb3JnYW5pemF0aW9uKVwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJkaXN0aW5jdF9pZFwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlBvc3RIb2cgZGlzdGluY3RfaWRcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgfSxcbiAgICAgIFwiZWxlbWVudHNfY2hhaW5cIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJVc2VkIGZvciBhdXRvY2FwdHVyZS4gRE9NIGVsZW1lbnQgaGllcmFyY2h5XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImNyZWF0ZWRfYXRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJUaW1lc3RhbXAgd2hlbiBldmVudCB3YXMgY3JlYXRlZFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJwZXJzb25faWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJVVUlEIGZvciB0aGUgYXNzb2NpYXRlZCBwZXJzb24gaWYgYXZhaWxhYmxlXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcInBlcnNvbl9jcmVhdGVkX2F0XCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiVGltZXN0YW1wIGZvciB3aGVuIHRoZSBhc3NvY2lhdGVkIHBlcnNvbiB3YXMgY3JlYXRlZFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJwZXJzb25fcHJvcGVydGllc1wiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgcGVyc29uIEpTT04gb2JqZWN0XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImdyb3VwMF9wcm9wZXJ0aWVzXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIGEgZ3JvdXAncyBwcm9wZXJ0aWVzXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImdyb3VwMV9wcm9wZXJ0aWVzXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIGEgZ3JvdXAncyBwcm9wZXJ0aWVzXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImdyb3VwMl9wcm9wZXJ0aWVzXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIGEgZ3JvdXAncyBwcm9wZXJ0aWVzXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImdyb3VwM19wcm9wZXJ0aWVzXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIGEgZ3JvdXAncyBwcm9wZXJ0aWVzXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImdyb3VwNF9wcm9wZXJ0aWVzXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiU3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIGEgZ3JvdXAncyBwcm9wZXJ0aWVzXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImdyb3VwMF9jcmVhdGVkX2F0XCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiR3JvdXAncyBjcmVhdGlvbiB0aW1lc3RhbXBcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgfSxcbiAgICAgIFwiZ3JvdXAxX2NyZWF0ZWRfYXRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJHcm91cCdzIGNyZWF0aW9uIHRpbWVzdGFtcFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJncm91cDJfY3JlYXRlZF9hdFwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIkdyb3VwJ3MgY3JlYXRpb24gdGltZXN0YW1wXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwibnVtYmVyXCJcbiAgICAgIH0sXG4gICAgICBcImdyb3VwM19jcmVhdGVkX2F0XCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiR3JvdXAncyBjcmVhdGlvbiB0aW1lc3RhbXBcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgfSxcbiAgICAgIFwiZ3JvdXA0X2NyZWF0ZWRfYXRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJHcm91cCdzIGNyZWF0aW9uIHRpbWVzdGFtcFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9XG4gIH0sXG4gIFwicmVxdWlyZWRcIjogW1widXVpZFwiLCBcImV2ZW50XCIsIFwicHJvcGVydGllc1wiLCBcInRpbWVzdGFtcFwiLCBcInRlYW1faWRcIl1cbn1cbiIKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vaWRsL2dyb3Vwcy5qc29uCiAgICAgICAgdGFyZ2V0OiAvaWRsL2dyb3Vwcy5qc29uCiAgICAgICAgY29udGVudDogIntcbiAgXCIkc2NoZW1hXCI6IFwiaHR0cHM6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQvMjAyMC0xMi9zY2hlbWFcIixcbiAgXCIkaWRcIjogXCJmaWxlOi8vcG9zdGhvZy9pZGwvZ3JvdXBzLmpzb25cIixcbiAgXCJ0aXRsZVwiOiBcImdyb3Vwc1wiLFxuICBcImRlc2NyaXB0aW9uXCI6IFwiR3JvdXBzIHNjaGVtYSB0aGF0IGlzIGRlc3RpbmVkIGZvciBDbGlja0hvdXNlXCIsXG4gIFwidHlwZVwiOiBcIm9iamVjdFwiLFxuICBcInByb3BlcnRpZXNcIjoge1xuICAgICAgXCJncm91cF90eXBlX2luZGV4XCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiR3JvdXAgdHlwZSBpbmRleFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJncm91cF9rZXlcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJHcm91cCBLZXlcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgfSxcbiAgICAgIFwiY3JlYXRlZF9hdFwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIkdyb3VwIGNyZWF0aW9uIHRpbWVzdGFtcFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJ0ZWFtX2lkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiVGVhbSBJRCBhc3NvY2lhdGVkIHdpdGggZ3JvdXBcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgfSxcbiAgICAgIFwiZ3JvdXBfcHJvcGVydGllc1wiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlN0cmluZyByZXByZXNlbnRhdGlvbiBvZiBncm91cCBKU09OIHByb3BlcnRpZXMgb2JqZWN0XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH1cbiAgfSxcbiAgXCJyZXF1aXJlZFwiOiBbXCJncm91cF90eXBlX2luZGV4XCIsIFwiZ3JvdXBfa2V5XCIsIFwiY3JlYXRlZF9hdFwiLCBcInRlYW1faWRcIiwgXCJncm91cF9wcm9wZXJ0aWVzXCJdXG59XG4iCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2lkbC9pZGwubWQKICAgICAgICB0YXJnZXQ6IC9pZGwvaWRsLm1kCiAgICAgICAgY29udGVudDogIiMgSURMIC0gSW50ZXJmYWNlIERlZmluaXRpb24gTGFuZ3VhZ2VcblxuVGhpcyBkaXJlY3RvcnkgaXMgcmVzcG9uc2libGUgZm9yIGRlZmluaW5nIHRoZSBzY2hlbWFzIG9mIHRoZSBkYXRhIGJldHdlZW4gc2VydmljZXMuXG5QcmltYXJpbHkgdGhpcyB3aWxsIGJlIGJldHdlZW4gc2VydmljZXMgYW5kIENsaWNrSG91c2UsIGJ1dCBjYW4gYmUgcmVhbGx5IGFueSB0aGluZyBhdCB0aGUgYm91bmRyeSBvZiBzZXJ2aWNlcy5cblxuVGhlIHJlYXNvbiB3aHkgd2UgZG8gdGhpcyBpcyBiZWNhdXNlIGl0IG1ha2VzIGdlbmVyYXRpbmcgY29kZSwgdmFsaWRhdGluZyBkYXRhLCBhbmQgdW5kZXJzdGFuZGluZyB0aGUgc3lzdGVtIGEgd2hvbGUgbG90IGVhc2llci4gV2UndmUgaGFkIGEgZmV3IGN1c3RvbWVycyByZXF1ZXN0IHRoaXMgb2YgdXMgZm9yIGVuZ2luZWVyaW5nIGEgZGVlcGVyIGludGVncmF0aW9uIHdpdGggdXMuXG4iCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2lkbC9wZXJzb24uanNvbgogICAgICAgIHRhcmdldDogL2lkbC9wZXJzb24uanNvbgogICAgICAgIGNvbnRlbnQ6ICJ7XG4gIFwiJHNjaGVtYVwiOiBcImh0dHBzOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LzIwMjAtMTIvc2NoZW1hXCIsXG4gIFwiJGlkXCI6IFwiZmlsZTovL3Bvc3Rob2cvaWRsL3BlcnNvbi5qc29uXCIsXG4gIFwidGl0bGVcIjogXCJwZXJzb25cIixcbiAgXCJkZXNjcmlwdGlvblwiOiBcIlBlcnNvbiBzY2hlbWEgdGhhdCBpcyBkZXN0aW5lZCBmb3IgQ2xpY2tIb3VzZVwiLFxuICBcInR5cGVcIjogXCJvYmplY3RcIixcbiAgXCJwcm9wZXJ0aWVzXCI6IHtcbiAgICAgIFwiaWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJVVUlEIGZvciB0aGUgcGVyc29uXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgIH0sXG4gICAgICBcImNyZWF0ZWRfYXRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJQZXJzb24gY3JlYXRpb24gdGltZXN0YW1wXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwibnVtYmVyXCJcbiAgICAgIH0sXG4gICAgICBcInRlYW1faWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJUZWFtIElEIGFzc29jaWF0ZWQgd2l0aCBwZXJzb25cIixcbiAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgfSxcbiAgICAgIFwicHJvcGVydGllc1wiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlN0cmluZyByZXByZXNlbnRhdGlvbiBvZiBwZXJzb24gSlNPTiBwcm9wZXJ0aWVzIG9iamVjdFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICB9LFxuICAgICAgXCJpc19pZGVudGlmaWVkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiQm9vbGVhbiBpcyB0aGUgcGVyc29uIGlkZW50aWZpZWQ\/XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwiYm9vbGVhblwiXG4gICAgICB9LFxuICAgICAgXCJpc19kZWxldGVkXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiQm9vbGVhbiBpcyB0aGUgcGVyc29uIGRlbGV0ZWQ\/XCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwiYm9vbGVhblwiXG4gICAgICB9LFxuICAgICAgXCJ2ZXJzaW9uXCI6IHtcbiAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiVmVyc2lvbiBmaWVsZCBmb3IgY29sbGFwc2luZyBsYXRlciAocHN1ZWRvLXRvbWJzdG9uZSlcIixcbiAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgfVxuICB9LFxuICBcInJlcXVpcmVkXCI6IFtcImlkXCIsIFwiY3JlYXRlZF9hdFwiLCBcInRlYW1faWRcIiwgXCJwcm9wZXJ0aWVzXCIsIFwiaXNfaWRlbnRpZmllZFwiLCBcImlzX2RlbGV0ZWRcIiwgXCJ2ZXJzaW9uXCJdXG59XG4iCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2lkbC9wZXJzb25fZGlzdGluY3RfaWQuanNvbgogICAgICAgIHRhcmdldDogL2lkbC9wZXJzb25fZGlzdGluY3RfaWQuanNvbgogICAgICAgIGNvbnRlbnQ6ICJ7XG4gIFwiJHNjaGVtYVwiOiBcImh0dHBzOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LzIwMjAtMTIvc2NoZW1hXCIsXG4gIFwiJGlkXCI6IFwiZmlsZTovL3Bvc3Rob2cvaWRsL3BlcnNvbl9kaXN0aW5jdF9pZC5qc29uXCIsXG4gIFwidGl0bGVcIjogXCJwZXJzb25fZGlzdGluY3RfaWRcIixcbiAgXCJkZXNjcmlwdGlvblwiOiBcIlBlcnNvbiBkaXN0aW5jdCBpZCBzY2hlbWEgdGhhdCBpcyBkZXN0aW5lZCBmb3IgQ2xpY2tIb3VzZVwiLFxuICBcInR5cGVcIjogXCJvYmplY3RcIixcbiAgXCJwcm9wZXJ0aWVzXCI6IHtcbiAgICAgIFwiZGlzdGluY3RfaWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJVc2VyIHByb3ZpZGVkIElEIGZvciB0aGUgZGlzdGluY3QgdXNlclwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICB9LFxuICAgICAgXCJwZXJzb25faWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJVVUlEIG9mIHRoZSBwZXJzb25cIixcbiAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgfSxcbiAgICAgIFwidGVhbV9pZFwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlRlYW0gSUQgYXNzb2NpYXRlZCB3aXRoIHBlcnNvbl9kaXN0aW5jdF9pZFwiLFxuICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICB9LFxuICAgICAgXCJfc2lnblwiOiB7XG4gICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlVzZWQgZm9yIGNvbGxhcHNpbmcgbGF0ZXIgZGlmZmVyZW50IHZlcnNpb25zIG9mIGEgZGlzdGluY3QgaWQgKHBzdWVkby10b21ic3RvbmUpXCIsXG4gICAgICAgICAgXCJ0eXBlXCI6IFwibnVtYmVyXCJcbiAgICAgIH0sXG4gICAgICBcImlzX2RlbGV0ZWRcIjoge1xuICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJCb29sZWFuIGlzIHRoZSBwZXJzb24gZGlzdGluY3RfaWQgZGVsZXRlZD9cIixcbiAgICAgICAgICBcInR5cGVcIjogXCJib29sZWFuXCJcbiAgICAgIH1cbiAgfSxcbiAgXCJyZXF1aXJlZFwiOiBbXCJkaXN0aW5jdF9pZFwiLCBcInBlcnNvbl9pZFwiLCBcInRlYW1faWRcIiwgXCJfc2lnblwiLCBcImlzX2RlbGV0ZWRcIl1cbiB9XG4iCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2lkbC9wZXJzb25fZGlzdGluY3RfaWQyLmpzb24KICAgICAgICB0YXJnZXQ6IC9pZGwvcGVyc29uX2Rpc3RpbmN0X2lkMi5qc29uCiAgICAgICAgY29udGVudDogIntcbiAgICBcIiRzY2hlbWFcIjogXCJodHRwczovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC8yMDIwLTEyL3NjaGVtYVwiLFxuICAgIFwiJGlkXCI6IFwiZmlsZTovL3Bvc3Rob2cvaWRsL3BlcnNvbl9kaXN0aW5jdF9pZDIuanNvblwiLFxuICAgIFwidGl0bGVcIjogXCJwZXJzb25fZGlzdGluY3RfaWQyXCIsXG4gICAgXCJkZXNjcmlwdGlvblwiOiBcIlBlcnNvbiBkaXN0aW5jdCBpZDIgc2NoZW1hIHRoYXQgaXMgZGVzdGluZWQgZm9yIENsaWNrSG91c2VcIixcbiAgICBcInR5cGVcIjogXCJvYmplY3RcIixcbiAgICBcInByb3BlcnRpZXNcIjoge1xuICAgICAgICBcImRpc3RpbmN0X2lkXCI6IHtcbiAgICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJVc2VyIHByb3ZpZGVkIElEIGZvciB0aGUgZGlzdGluY3QgdXNlclwiLFxuICAgICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgICAgfSxcbiAgICAgICAgXCJwZXJzb25faWRcIjoge1xuICAgICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlVVSUQgb2YgdGhlIHBlcnNvblwiLFxuICAgICAgICAgICAgXCJ0eXBlXCI6IFwic3RyaW5nXCJcbiAgICAgICAgfSxcbiAgICAgICAgXCJ0ZWFtX2lkXCI6IHtcbiAgICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJUZWFtIElEIGFzc29jaWF0ZWQgd2l0aCBwZXJzb25fZGlzdGluY3RfaWRcIixcbiAgICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICAgIH0sXG4gICAgICAgIFwidmVyc2lvblwiOiB7XG4gICAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiVXNlZCBmb3IgY29sbGFwc2luZyBsYXRlciBkaWZmZXJlbnQgdmVyc2lvbnMgb2YgYSBkaXN0aW5jdCBpZCAocHN1ZWRvLXRvbWJzdG9uZSlcIixcbiAgICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICAgIH0sXG4gICAgICAgIFwiaXNfZGVsZXRlZFwiOiB7XG4gICAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiQm9vbGVhbiBpcyB0aGUgcGVyc29uIGRpc3RpbmN0X2lkIGRlbGV0ZWQ\/XCIsXG4gICAgICAgICAgICBcInR5cGVcIjogXCJib29sZWFuXCJcbiAgICAgICAgfVxuICAgIH0sXG4gICAgXCJyZXF1aXJlZFwiOiBbXCJkaXN0aW5jdF9pZFwiLCBcInBlcnNvbl9pZFwiLCBcInRlYW1faWRcIiwgXCJ2ZXJzaW9uXCIsIFwiaXNfZGVsZXRlZFwiXVxufVxuIgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9pZGwvcGx1Z2luX2xvZ19lbnRyaWVzLmpzb24KICAgICAgICB0YXJnZXQ6IC9pZGwvcGx1Z2luX2xvZ19lbnRyaWVzLmpzb24KICAgICAgICBjb250ZW50OiAie1xuICAgIFwiJHNjaGVtYVwiOiBcImh0dHBzOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LzIwMjAtMTIvc2NoZW1hXCIsXG4gICAgXCIkaWRcIjogXCJmaWxlOi8vcG9zdGhvZy9pZGwvcGx1Z2luX2xvZ19lbnRyaWVzLmpzb25cIixcbiAgICBcInRpdGxlXCI6IFwicGx1Z2luX2xvZ19lbnRyaWVzXCIsXG4gICAgXCJkZXNjcmlwdGlvblwiOiBcIlBsdWdpbiBsb2cgZW50cmllcyB0aGF0IGFyZSBkZXN0aW5lZCBmb3IgQ2xpY2tIb3VzZVwiLFxuICAgIFwidHlwZVwiOiBcIm9iamVjdFwiLFxuICAgIFwicHJvcGVydGllc1wiOiB7XG4gICAgICAgIFwiaWRcIjoge1xuICAgICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlVVSUQgZm9yIHRoZSBsb2cgZW50cnlcIixcbiAgICAgICAgICAgIFwidHlwZVwiOiBcInN0cmluZ1wiXG4gICAgICAgIH0sXG4gICAgICAgIFwidGVhbV9pZFwiOiB7XG4gICAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiVGVhbSBJRCBhc3NvY2lhdGVkIHdpdGggcGVyc29uX2Rpc3RpbmN0X2lkXCIsXG4gICAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgICB9LFxuICAgICAgICBcInBsdWdpbl9pZFwiOiB7XG4gICAgICAgICAgICBcImRlc2NyaXB0aW9uXCI6IFwiUGx1Z2luIElEIGFzc29jaWF0ZWQgd2l0aCB0aGUgbG9nIGVudHJ5XCIsXG4gICAgICAgICAgICBcInR5cGVcIjogXCJudW1iZXJcIlxuICAgICAgICB9LFxuICAgICAgICBcInBsdWdpbl9jb25maWdfaWRcIjoge1xuICAgICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlBsdWdpbiBDb25maWcgSUQgYXNzb2NpYXRlZCB3aXRoIHRoZSBsb2cgZW50cnlcIixcbiAgICAgICAgICAgIFwidHlwZVwiOiBcIm51bWJlclwiXG4gICAgICAgIH0sXG4gICAgICAgIFwidGltZXN0YW1wXCI6IHtcbiAgICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJUaW1lc3RhbXAgZm9yIHdoZW4gdGhlIGxvZyBlbnRyeSB3YXMgY3JlYXRlZFwiLFxuICAgICAgICAgICAgXCJ0eXBlXCI6IFwibnVtYmVyXCJcbiAgICAgICAgfSxcbiAgICAgICAgXCJzb3VyY2VcIjoge1xuICAgICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIlNvdXJjZSBvZiB0aGUgbG9nIGVudHJ5XCIsXG4gICAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgICB9LFxuICAgICAgICBcInR5cGVcIjoge1xuICAgICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIkxvZyBlbnRyeSB0eXBlXCIsXG4gICAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgICB9LFxuICAgICAgICBcIm1lc3NhZ2VcIjoge1xuICAgICAgICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIkxvZyBlbnRyeSBib2R5XCIsXG4gICAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgICB9LFxuICAgICAgICBcImluc3RhbmNlX2lkXCI6IHtcbiAgICAgICAgICAgIFwiZGVzY3JpcHRpb25cIjogXCJVVUlEIG9mIHRoZSBpbnN0YW5jZSB0aGF0IGdlbmVyYXRlZCB0aGUgbG9nIGVudHJ5XCIsXG4gICAgICAgICAgICBcInR5cGVcIjogXCJzdHJpbmdcIlxuICAgICAgICB9XG4gICAgfSxcbiAgICBcInJlcXVpcmVkXCI6IFtcbiAgICAgICAgXCJpZFwiLFxuICAgICAgICBcInRlYW1faWRcIixcbiAgICAgICAgXCJwbHVnaW5faWRcIixcbiAgICAgICAgXCJwbHVnaW5fY29uZmlnX2lkXCIsXG4gICAgICAgIFwidGltZXN0YW1wXCIsXG4gICAgICAgIFwic291cmNlXCIsXG4gICAgICAgIFwidHlwZVwiLFxuICAgICAgICBcIm1lc3NhZ2VcIixcbiAgICAgICAgXCJpbnN0YW5jZV9pZFwiXG4gICAgXVxufVxuIgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9kb2NrZXIvY2xpY2tob3VzZS9kb2NrZXItZW50cnlwb2ludC1pbml0ZGIuZC9pbml0LWRiLnNoCiAgICAgICAgdGFyZ2V0OiAvZG9ja2VyLWVudHJ5cG9pbnQtaW5pdGRiLmQvaW5pdC1kYi5zaAogICAgICAgIGNvbnRlbnQ6ICIjIS9iaW4vYmFzaFxuc2V0IC1lXG5cbmNwIC1yIC9pZGwvKiAvdmFyL2xpYi9jbGlja2hvdXNlL2Zvcm1hdF9zY2hlbWFzL1xuIgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9kb2NrZXIvY2xpY2tob3VzZS9jb25maWcueG1sCiAgICAgICAgdGFyZ2V0OiAvZXRjL2NsaWNraG91c2Utc2VydmVyL2NvbmZpZy54bWwKICAgICAgICBjb250ZW50OiAiPD94bWwgdmVyc2lvbj1cIjEuMFwiPz5cbjwhLS1cbiAgTk9URTogVXNlciBhbmQgcXVlcnkgbGV2ZWwgc2V0dGluZ3MgYXJlIHNldCB1cCBpbiBcInVzZXJzLnhtbFwiIGZpbGUuXG4gIElmIHlvdSBoYXZlIGFjY2lkZW50YWxseSBzcGVjaWZpZWQgdXNlci1sZXZlbCBzZXR0aW5ncyBoZXJlLCBzZXJ2ZXIgd29uJ3Qgc3RhcnQuXG4gIFlvdSBjYW4gZWl0aGVyIG1vdmUgdGhlIHNldHRpbmdzIHRvIHRoZSByaWdodCBwbGFjZSBpbnNpZGUgXCJ1c2Vycy54bWxcIiBmaWxlXG4gIG9yIGFkZCA8c2tpcF9jaGVja19mb3JfaW5jb3JyZWN0X3NldHRpbmdzPjE8L3NraXBfY2hlY2tfZm9yX2luY29ycmVjdF9zZXR0aW5ncz4gaGVyZS5cbi0tPlxuPHlhbmRleD5cbiAgICA8bG9nZ2VyPlxuICAgICAgICA8IS0tIFBvc3NpYmxlIGxldmVscyBbMV06XG5cbiAgICAgICAgICAtIG5vbmUgKHR1cm5zIG9mZiBsb2dnaW5nKVxuICAgICAgICAgIC0gZmF0YWxcbiAgICAgICAgICAtIGNyaXRpY2FsXG4gICAgICAgICAgLSBlcnJvclxuICAgICAgICAgIC0gd2FybmluZ1xuICAgICAgICAgIC0gbm90aWNlXG4gICAgICAgICAgLSBpbmZvcm1hdGlvblxuICAgICAgICAgIC0gZGVidWdcbiAgICAgICAgICAtIHRyYWNlXG4gICAgICAgICAgLSB0ZXN0IChub3QgZm9yIHByb2R1Y3Rpb24gdXNhZ2UpXG5cbiAgICAgICAgICAgIFsxXTpcbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL3BvY29wcm9qZWN0L3BvY28vYmxvYi9wb2NvLTEuOS40LXJlbGVhc2UvRm91bmRhdGlvbi9pbmNsdWRlL1BvY28vTG9nZ2VyLmgjTDEwNS1MMTE0XG4gICAgICAgIC0tPlxuICAgICAgICA8bGV2ZWw+dHJhY2U8L2xldmVsPlxuICAgICAgICA8bG9nPi92YXIvbG9nL2NsaWNraG91c2Utc2VydmVyL2NsaWNraG91c2Utc2VydmVyLmxvZzwvbG9nPlxuICAgICAgICA8ZXJyb3Jsb2c+L3Zhci9sb2cvY2xpY2tob3VzZS1zZXJ2ZXIvY2xpY2tob3VzZS1zZXJ2ZXIuZXJyLmxvZzwvZXJyb3Jsb2c+XG4gICAgICAgIDwhLS0gUm90YXRpb24gcG9saWN5XG4gICAgICAgICAgICBTZWVcbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL3BvY29wcm9qZWN0L3BvY28vYmxvYi9wb2NvLTEuOS40LXJlbGVhc2UvRm91bmRhdGlvbi9pbmNsdWRlL1BvY28vRmlsZUNoYW5uZWwuaCNMNTQtTDg1XG4gICAgICAgICAgLS0+XG4gICAgICAgIDxzaXplPjEwMDBNPC9zaXplPlxuICAgICAgICA8Y291bnQ+MTA8L2NvdW50PlxuICAgICAgICA8IS0tIDxjb25zb2xlPjE8L2NvbnNvbGU+IC0tPiA8IS0tIERlZmF1bHQgYmVoYXZpb3IgaXMgYXV0b2RldGVjdGlvbiAobG9nIHRvIGNvbnNvbGUgaWYgbm90IGRhZW1vbiBtb2RlXG4gICAgICAgIGFuZCBpcyB0dHkpIC0tPlxuXG4gICAgICAgIDwhLS0gUGVyIGxldmVsIG92ZXJyaWRlcyAobGVnYWN5KTpcblxuICAgICAgICBGb3IgZXhhbXBsZSB0byBzdXBwcmVzcyBsb2dnaW5nIG9mIHRoZSBDb25maWdSZWxvYWRlciB5b3UgY2FuIHVzZTpcbiAgICAgICAgTk9URTogbGV2ZWxzLmxvZ2dlciBpcyByZXNlcnZlZCwgc2VlIGJlbG93LlxuICAgICAgICAtLT5cbiAgICAgICAgPCEtLVxuICAgICAgICA8bGV2ZWxzPlxuICAgICAgICAgIDxDb25maWdSZWxvYWRlcj5ub25lPC9Db25maWdSZWxvYWRlcj5cbiAgICAgICAgPC9sZXZlbHM+XG4gICAgICAgIC0tPlxuXG4gICAgICAgIDwhLS0gUGVyIGxldmVsIG92ZXJyaWRlczpcblxuICAgICAgICBGb3IgZXhhbXBsZSB0byBzdXBwcmVzcyBsb2dnaW5nIG9mIHRoZSBSQkFDIGZvciBkZWZhdWx0IHVzZXIgeW91IGNhbiB1c2U6XG4gICAgICAgIChCdXQgcGxlYXNlIG5vdGUgdGhhdCB0aGUgbG9nZ2VyIG5hbWUgbWF5YmUgY2hhbmdlZCBmcm9tIHZlcnNpb24gdG8gdmVyc2lvbiwgZXZlbiBhZnRlciBtaW5vclxuICAgICAgICB1cGdyYWRlKVxuICAgICAgICAtLT5cbiAgICAgICAgPCEtLVxuICAgICAgICA8bGV2ZWxzPlxuICAgICAgICAgIDxsb2dnZXI+XG4gICAgICAgICAgICA8bmFtZT5Db250ZXh0QWNjZXNzIChkZWZhdWx0KTwvbmFtZT5cbiAgICAgICAgICAgIDxsZXZlbD5ub25lPC9sZXZlbD5cbiAgICAgICAgICA8L2xvZ2dlcj5cbiAgICAgICAgICA8bG9nZ2VyPlxuICAgICAgICAgICAgPG5hbWU+RGF0YWJhc2VPcmRpbmFyeSAodGVzdCk8L25hbWU+XG4gICAgICAgICAgICA8bGV2ZWw+bm9uZTwvbGV2ZWw+XG4gICAgICAgICAgPC9sb2dnZXI+XG4gICAgICAgIDwvbGV2ZWxzPlxuICAgICAgICAtLT5cbiAgICA8L2xvZ2dlcj5cblxuICAgIDwhLS0gQWRkIGhlYWRlcnMgdG8gcmVzcG9uc2UgaW4gb3B0aW9ucyByZXF1ZXN0LiBPUFRJT05TIG1ldGhvZCBpcyB1c2VkIGluIENPUlMgcHJlZmxpZ2h0XG4gICAgcmVxdWVzdHMuIC0tPlxuICAgIDwhLS0gSXQgaXMgb2ZmIGJ5IGRlZmF1bHQuIE5leHQgaGVhZGVycyBhcmUgb2JsaWdhdGUgZm9yIENPUlMuLS0+XG4gICAgPCEtLSBodHRwX29wdGlvbnNfcmVzcG9uc2U+XG4gICAgICAgIDxoZWFkZXI+XG4gICAgICAgICAgICA8bmFtZT5BY2Nlc3MtQ29udHJvbC1BbGxvdy1PcmlnaW48L25hbWU+XG4gICAgICAgICAgICA8dmFsdWU+KjwvdmFsdWU+XG4gICAgICAgIDwvaGVhZGVyPlxuICAgICAgICA8aGVhZGVyPlxuICAgICAgICAgICAgPG5hbWU+QWNjZXNzLUNvbnRyb2wtQWxsb3ctSGVhZGVyczwvbmFtZT5cbiAgICAgICAgICAgIDx2YWx1ZT5vcmlnaW4sIHgtcmVxdWVzdGVkLXdpdGg8L3ZhbHVlPlxuICAgICAgICA8L2hlYWRlcj5cbiAgICAgICAgPGhlYWRlcj5cbiAgICAgICAgICAgIDxuYW1lPkFjY2Vzcy1Db250cm9sLUFsbG93LU1ldGhvZHM8L25hbWU+XG4gICAgICAgICAgICA8dmFsdWU+UE9TVCwgR0VULCBPUFRJT05TPC92YWx1ZT5cbiAgICAgICAgPC9oZWFkZXI+XG4gICAgICAgIDxoZWFkZXI+XG4gICAgICAgICAgICA8bmFtZT5BY2Nlc3MtQ29udHJvbC1NYXgtQWdlPC9uYW1lPlxuICAgICAgICAgICAgPHZhbHVlPjg2NDAwPC92YWx1ZT5cbiAgICAgICAgPC9oZWFkZXI+XG4gICAgPC9odHRwX29wdGlvbnNfcmVzcG9uc2UgLS0+XG5cbiAgICA8IS0tIEl0IGlzIHRoZSBuYW1lIHRoYXQgd2lsbCBiZSBzaG93biBpbiB0aGUgY2xpY2tob3VzZS1jbGllbnQuXG4gICAgICAgIEJ5IGRlZmF1bHQsIGFueXRoaW5nIHdpdGggXCJwcm9kdWN0aW9uXCIgd2lsbCBiZSBoaWdobGlnaHRlZCBpbiByZWQgaW4gcXVlcnkgcHJvbXB0LlxuICAgIC0tPlxuICAgIDwhLS1kaXNwbGF5X25hbWU+cHJvZHVjdGlvbjwvZGlzcGxheV9uYW1lLS0+XG5cbiAgICA8IS0tIFBvcnQgZm9yIEhUVFAgQVBJLiBTZWUgYWxzbyAnaHR0cHNfcG9ydCcgZm9yIHNlY3VyZSBjb25uZWN0aW9ucy5cbiAgICAgICAgVGhpcyBpbnRlcmZhY2UgaXMgYWxzbyB1c2VkIGJ5IE9EQkMgYW5kIEpEQkMgZHJpdmVycyAoRGF0YUdyaXAsIERiZWF2ZXIsIC4uLilcbiAgICAgICAgYW5kIGJ5IG1vc3Qgb2Ygd2ViIGludGVyZmFjZXMgKGVtYmVkZGVkIFVJLCBHcmFmYW5hLCBSZWRhc2gsIC4uLikuXG4gICAgICAtLT5cbiAgICA8aHR0cF9wb3J0PjgxMjM8L2h0dHBfcG9ydD5cblxuICAgIDwhLS0gUG9ydCBmb3IgaW50ZXJhY3Rpb24gYnkgbmF0aXZlIHByb3RvY29sIHdpdGg6XG4gICAgICAgIC0gY2xpY2tob3VzZS1jbGllbnQgYW5kIG90aGVyIG5hdGl2ZSBDbGlja0hvdXNlIHRvb2xzIChjbGlja2hvdXNlLWJlbmNobWFyaywgY2xpY2tob3VzZS1jb3BpZXIpO1xuICAgICAgICAtIGNsaWNraG91c2Utc2VydmVyIHdpdGggb3RoZXIgY2xpY2tob3VzZS1zZXJ2ZXJzIGZvciBkaXN0cmlidXRlZCBxdWVyeSBwcm9jZXNzaW5nO1xuICAgICAgICAtIENsaWNrSG91c2UgZHJpdmVycyBhbmQgYXBwbGljYXRpb25zIHN1cHBvcnRpbmcgbmF0aXZlIHByb3RvY29sXG4gICAgICAgICh0aGlzIHByb3RvY29sIGlzIGFsc28gaW5mb3JtYWxseSBjYWxsZWQgYXMgXCJ0aGUgVENQIHByb3RvY29sXCIpO1xuICAgICAgICBTZWUgYWxzbyAndGNwX3BvcnRfc2VjdXJlJyBmb3Igc2VjdXJlIGNvbm5lY3Rpb25zLlxuICAgIC0tPlxuICAgIDx0Y3BfcG9ydD45MDAwPC90Y3BfcG9ydD5cblxuICAgIDwhLS0gQ29tcGF0aWJpbGl0eSB3aXRoIE15U1FMIHByb3RvY29sLlxuICAgICAgICBDbGlja0hvdXNlIHdpbGwgcHJldGVuZCB0byBiZSBNeVNRTCBmb3IgYXBwbGljYXRpb25zIGNvbm5lY3RpbmcgdG8gdGhpcyBwb3J0LlxuICAgIC0tPlxuICAgIDxteXNxbF9wb3J0PjkwMDQ8L215c3FsX3BvcnQ+XG5cbiAgICA8IS0tIENvbXBhdGliaWxpdHkgd2l0aCBQb3N0Z3JlU1FMIHByb3RvY29sLlxuICAgICAgICBDbGlja0hvdXNlIHdpbGwgcHJldGVuZCB0byBiZSBQb3N0Z3JlU1FMIGZvciBhcHBsaWNhdGlvbnMgY29ubmVjdGluZyB0byB0aGlzIHBvcnQuXG4gICAgLS0+XG4gICAgPHBvc3RncmVzcWxfcG9ydD45MDA1PC9wb3N0Z3Jlc3FsX3BvcnQ+XG5cbiAgICA8IS0tIEhUVFAgQVBJIHdpdGggVExTIChIVFRQUykuXG4gICAgICAgIFlvdSBoYXZlIHRvIGNvbmZpZ3VyZSBjZXJ0aWZpY2F0ZSB0byBlbmFibGUgdGhpcyBpbnRlcmZhY2UuXG4gICAgICAgIFNlZSB0aGUgb3BlblNTTCBzZWN0aW9uIGJlbG93LlxuICAgIC0tPlxuICAgIDxodHRwc19wb3J0Pjg0NDM8L2h0dHBzX3BvcnQ+XG5cbiAgICA8IS0tIE5hdGl2ZSBpbnRlcmZhY2Ugd2l0aCBUTFMuXG4gICAgICAgIFlvdSBoYXZlIHRvIGNvbmZpZ3VyZSBjZXJ0aWZpY2F0ZSB0byBlbmFibGUgdGhpcyBpbnRlcmZhY2UuXG4gICAgICAgIFNlZSB0aGUgb3BlblNTTCBzZWN0aW9uIGJlbG93LlxuICAgIC0tPlxuICAgIDx0Y3BfcG9ydF9zZWN1cmU+OTQ0MDwvdGNwX3BvcnRfc2VjdXJlPlxuXG4gICAgPCEtLSBOYXRpdmUgaW50ZXJmYWNlIHdyYXBwZWQgd2l0aCBQUk9YWXYxIHByb3RvY29sXG4gICAgICAgIFBST1hZdjEgaGVhZGVyIHNlbnQgZm9yIGV2ZXJ5IGNvbm5lY3Rpb24uXG4gICAgICAgIENsaWNrSG91c2Ugd2lsbCBleHRyYWN0IGluZm9ybWF0aW9uIGFib3V0IHByb3h5LWZvcndhcmRlZCBjbGllbnQgYWRkcmVzcyBmcm9tIHRoZSBoZWFkZXIuXG4gICAgLS0+XG4gICAgPCEtLSA8dGNwX3dpdGhfcHJveHlfcG9ydD45MDExPC90Y3Bfd2l0aF9wcm94eV9wb3J0PiAtLT5cblxuICAgIDwhLS0gUG9ydCBmb3IgY29tbXVuaWNhdGlvbiBiZXR3ZWVuIHJlcGxpY2FzLiBVc2VkIGZvciBkYXRhIGV4Y2hhbmdlLlxuICAgICAgICBJdCBwcm92aWRlcyBsb3ctbGV2ZWwgZGF0YSBhY2Nlc3MgYmV0d2VlbiBzZXJ2ZXJzLlxuICAgICAgICBUaGlzIHBvcnQgc2hvdWxkIG5vdCBiZSBhY2Nlc3NpYmxlIGZyb20gdW50cnVzdGVkIG5ldHdvcmtzLlxuICAgICAgICBTZWUgYWxzbyAnaW50ZXJzZXJ2ZXJfaHR0cF9jcmVkZW50aWFscycuXG4gICAgICAgIERhdGEgdHJhbnNmZXJyZWQgb3ZlciBjb25uZWN0aW9ucyB0byB0aGlzIHBvcnQgc2hvdWxkIG5vdCBnbyB0aHJvdWdoIHVudHJ1c3RlZCBuZXR3b3Jrcy5cbiAgICAgICAgU2VlIGFsc28gJ2ludGVyc2VydmVyX2h0dHBzX3BvcnQnLlxuICAgICAgLS0+XG4gICAgPGludGVyc2VydmVyX2h0dHBfcG9ydD45MDA5PC9pbnRlcnNlcnZlcl9odHRwX3BvcnQ+XG5cbiAgICA8IS0tIFBvcnQgZm9yIGNvbW11bmljYXRpb24gYmV0d2VlbiByZXBsaWNhcyB3aXRoIFRMUy5cbiAgICAgICAgWW91IGhhdmUgdG8gY29uZmlndXJlIGNlcnRpZmljYXRlIHRvIGVuYWJsZSB0aGlzIGludGVyZmFjZS5cbiAgICAgICAgU2VlIHRoZSBvcGVuU1NMIHNlY3Rpb24gYmVsb3cuXG4gICAgICAgIFNlZSBhbHNvICdpbnRlcnNlcnZlcl9odHRwX2NyZWRlbnRpYWxzJy5cbiAgICAgIC0tPlxuICAgIDwhLS0gPGludGVyc2VydmVyX2h0dHBzX3BvcnQ+OTAxMDwvaW50ZXJzZXJ2ZXJfaHR0cHNfcG9ydD4gLS0+XG5cbiAgICA8IS0tIEhvc3RuYW1lIHRoYXQgaXMgdXNlZCBieSBvdGhlciByZXBsaWNhcyB0byByZXF1ZXN0IHRoaXMgc2VydmVyLlxuICAgICAgICBJZiBub3Qgc3BlY2lmaWVkLCB0aGFuIGl0IGlzIGRldGVybWluZWQgYW5hbG9nb3VzIHRvICdob3N0bmFtZSAtZicgY29tbWFuZC5cbiAgICAgICAgVGhpcyBzZXR0aW5nIGNvdWxkIGJlIHVzZWQgdG8gc3dpdGNoIHJlcGxpY2F0aW9uIHRvIGFub3RoZXIgbmV0d29yayBpbnRlcmZhY2VcbiAgICAgICAgKHRoZSBzZXJ2ZXIgbWF5IGJlIGNvbm5lY3RlZCB0byBtdWx0aXBsZSBuZXR3b3JrcyB2aWEgbXVsdGlwbGUgYWRkcmVzc2VzKVxuICAgICAgLS0+XG5cbiAgICA8IS0tXG4gICAgPGludGVyc2VydmVyX2h0dHBfaG9zdD5leGFtcGxlLnlhbmRleC5ydTwvaW50ZXJzZXJ2ZXJfaHR0cF9ob3N0PlxuICAgIC0tPlxuXG4gICAgPCEtLSBZb3UgY2FuIHNwZWNpZnkgY3JlZGVudGlhbHMgZm9yIGF1dGhlbnRoaWNhdGlvbiBiZXR3ZWVuIHJlcGxpY2FzLlxuICAgICAgICBUaGlzIGlzIHJlcXVpcmVkIHdoZW4gaW50ZXJzZXJ2ZXJfaHR0cHNfcG9ydCBpcyBhY2Nlc3NpYmxlIGZyb20gdW50cnVzdGVkIG5ldHdvcmtzLFxuICAgICAgICBhbmQgYWxzbyByZWNvbW1lbmRlZCB0byBhdm9pZCBTU1JGIGF0dGFja3MgZnJvbSBwb3NzaWJseSBjb21wcm9taXNlZCBzZXJ2aWNlcyBpbiB5b3VyIG5ldHdvcmsuXG4gICAgICAtLT5cbiAgICA8IS0tPGludGVyc2VydmVyX2h0dHBfY3JlZGVudGlhbHM+XG4gICAgICAgIDx1c2VyPmludGVyc2VydmVyPC91c2VyPlxuICAgICAgICA8cGFzc3dvcmQ+PC9wYXNzd29yZD5cbiAgICA8L2ludGVyc2VydmVyX2h0dHBfY3JlZGVudGlhbHM+LS0+XG5cbiAgICA8IS0tIExpc3RlbiBzcGVjaWZpZWQgYWRkcmVzcy5cbiAgICAgICAgVXNlIDo6ICh3aWxkY2FyZCBJUHY2IGFkZHJlc3MpLCBpZiB5b3Ugd2FudCB0byBhY2NlcHQgY29ubmVjdGlvbnMgYm90aCB3aXRoIElQdjQgYW5kIElQdjYgZnJvbVxuICAgIGV2ZXJ5d2hlcmUuXG4gICAgICAgIE5vdGVzOlxuICAgICAgICBJZiB5b3Ugb3BlbiBjb25uZWN0aW9ucyBmcm9tIHdpbGRjYXJkIGFkZHJlc3MsIG1ha2Ugc3VyZSB0aGF0IGF0IGxlYXN0IG9uZSBvZiB0aGUgZm9sbG93aW5nXG4gICAgbWVhc3VyZXMgYXBwbGllZDpcbiAgICAgICAgLSBzZXJ2ZXIgaXMgcHJvdGVjdGVkIGJ5IGZpcmV3YWxsIGFuZCBub3QgYWNjZXNzaWJsZSBmcm9tIHVudHJ1c3RlZCBuZXR3b3JrcztcbiAgICAgICAgLSBhbGwgdXNlcnMgYXJlIHJlc3RyaWN0ZWQgdG8gc3Vic2V0IG9mIG5ldHdvcmsgYWRkcmVzc2VzIChzZWUgdXNlcnMueG1sKTtcbiAgICAgICAgLSBhbGwgdXNlcnMgaGF2ZSBzdHJvbmcgcGFzc3dvcmRzLCBvbmx5IHNlY3VyZSAoVExTKSBpbnRlcmZhY2VzIGFyZSBhY2Nlc3NpYmxlLCBvciBjb25uZWN0aW9ucyBhcmVcbiAgICBvbmx5IG1hZGUgdmlhIFRMUyBpbnRlcmZhY2VzLlxuICAgICAgICAtIHVzZXJzIHdpdGhvdXQgcGFzc3dvcmQgaGF2ZSByZWFkb25seSBhY2Nlc3MuXG4gICAgICAgIFNlZSBhbHNvOiBodHRwczovL3d3dy5zaG9kYW4uaW8vc2VhcmNoP3F1ZXJ5PWNsaWNraG91c2VcbiAgICAgIC0tPlxuICAgIDwhLS0gPGxpc3Rlbl9ob3N0Pjo6PC9saXN0ZW5faG9zdD4gLS0+XG5cblxuICAgIDwhLS0gU2FtZSBmb3IgaG9zdHMgd2l0aG91dCBzdXBwb3J0IGZvciBJUHY2OiAtLT5cbiAgICA8IS0tIDxsaXN0ZW5faG9zdD4wLjAuMC4wPC9saXN0ZW5faG9zdD4gLS0+XG5cbiAgICA8IS0tIERlZmF1bHQgdmFsdWVzIC0gdHJ5IGxpc3RlbiBsb2NhbGhvc3Qgb24gSVB2NCBhbmQgSVB2Ni4gLS0+XG4gICAgPCEtLVxuICAgIDxsaXN0ZW5faG9zdD46OjE8L2xpc3Rlbl9ob3N0PlxuICAgIDxsaXN0ZW5faG9zdD4xMjcuMC4wLjE8L2xpc3Rlbl9ob3N0PlxuICAgIC0tPlxuXG4gICAgPCEtLSBEb24ndCBleGl0IGlmIElQdjYgb3IgSVB2NCBuZXR3b3JrcyBhcmUgdW5hdmFpbGFibGUgd2hpbGUgdHJ5aW5nIHRvIGxpc3Rlbi4gLS0+XG4gICAgPCEtLSA8bGlzdGVuX3RyeT4wPC9saXN0ZW5fdHJ5PiAtLT5cblxuICAgIDwhLS0gQWxsb3cgbXVsdGlwbGUgc2VydmVycyB0byBsaXN0ZW4gb24gdGhlIHNhbWUgYWRkcmVzczpwb3J0LiBUaGlzIGlzIG5vdCByZWNvbW1lbmRlZC5cbiAgICAgIC0tPlxuICAgIDwhLS0gPGxpc3Rlbl9yZXVzZV9wb3J0PjA8L2xpc3Rlbl9yZXVzZV9wb3J0PiAtLT5cblxuICAgIDwhLS0gPGxpc3Rlbl9iYWNrbG9nPjQwOTY8L2xpc3Rlbl9iYWNrbG9nPiAtLT5cblxuICAgIDxtYXhfY29ubmVjdGlvbnM+NDA5NjwvbWF4X2Nvbm5lY3Rpb25zPlxuXG4gICAgPCEtLSBGb3IgJ0Nvbm5lY3Rpb246IGtlZXAtYWxpdmUnIGluIEhUVFAgMS4xIC0tPlxuICAgIDxrZWVwX2FsaXZlX3RpbWVvdXQ+Mzwva2VlcF9hbGl2ZV90aW1lb3V0PlxuXG4gICAgPCEtLSBnUlBDIHByb3RvY29sIChzZWUgc3JjL1NlcnZlci9ncnBjX3Byb3Rvcy9jbGlja2hvdXNlX2dycGMucHJvdG8gZm9yIHRoZSBBUEkpIC0tPlxuICAgIDwhLS0gPGdycGNfcG9ydD45MTAwPC9ncnBjX3BvcnQ+IC0tPlxuICAgIDxncnBjPlxuICAgICAgICA8ZW5hYmxlX3NzbD5mYWxzZTwvZW5hYmxlX3NzbD5cblxuICAgICAgICA8IS0tIFRoZSBmb2xsb3dpbmcgdHdvIGZpbGVzIGFyZSB1c2VkIG9ubHkgaWYgZW5hYmxlX3NzbD0xIC0tPlxuICAgICAgICA8c3NsX2NlcnRfZmlsZT4vcGF0aC90by9zc2xfY2VydF9maWxlPC9zc2xfY2VydF9maWxlPlxuICAgICAgICA8c3NsX2tleV9maWxlPi9wYXRoL3RvL3NzbF9rZXlfZmlsZTwvc3NsX2tleV9maWxlPlxuXG4gICAgICAgIDwhLS0gV2hldGhlciBzZXJ2ZXIgd2lsbCByZXF1ZXN0IGNsaWVudCBmb3IgYSBjZXJ0aWZpY2F0ZSAtLT5cbiAgICAgICAgPHNzbF9yZXF1aXJlX2NsaWVudF9hdXRoPmZhbHNlPC9zc2xfcmVxdWlyZV9jbGllbnRfYXV0aD5cblxuICAgICAgICA8IS0tIFRoZSBmb2xsb3dpbmcgZmlsZSBpcyB1c2VkIG9ubHkgaWYgc3NsX3JlcXVpcmVfY2xpZW50X2F1dGg9MSAtLT5cbiAgICAgICAgPHNzbF9jYV9jZXJ0X2ZpbGU+L3BhdGgvdG8vc3NsX2NhX2NlcnRfZmlsZTwvc3NsX2NhX2NlcnRfZmlsZT5cblxuICAgICAgICA8IS0tIERlZmF1bHQgdHJhbnNwb3J0IGNvbXByZXNzaW9uIHR5cGUgKGNhbiBiZSBvdmVycmlkZGVuIGJ5IGNsaWVudCwgc2VlIHRoZVxuICAgICAgICB0cmFuc3BvcnRfY29tcHJlc3Npb25fdHlwZSBmaWVsZCBpbiBRdWVyeUluZm8pLlxuICAgICAgICAgICAgU3VwcG9ydGVkIGFsZ29yaXRobXM6IG5vbmUsIGRlZmxhdGUsIGd6aXAsIHN0cmVhbV9nemlwIC0tPlxuICAgICAgICA8dHJhbnNwb3J0X2NvbXByZXNzaW9uX3R5cGU+bm9uZTwvdHJhbnNwb3J0X2NvbXByZXNzaW9uX3R5cGU+XG5cbiAgICAgICAgPCEtLSBEZWZhdWx0IHRyYW5zcG9ydCBjb21wcmVzc2lvbiBsZXZlbC4gU3VwcG9ydGVkIGxldmVsczogMC4uMyAtLT5cbiAgICAgICAgPHRyYW5zcG9ydF9jb21wcmVzc2lvbl9sZXZlbD4wPC90cmFuc3BvcnRfY29tcHJlc3Npb25fbGV2ZWw+XG5cbiAgICAgICAgPCEtLSBTZW5kL3JlY2VpdmUgbWVzc2FnZSBzaXplIGxpbWl0cyBpbiBieXRlcy4gLTEgbWVhbnMgdW5saW1pdGVkIC0tPlxuICAgICAgICA8bWF4X3NlbmRfbWVzc2FnZV9zaXplPi0xPC9tYXhfc2VuZF9tZXNzYWdlX3NpemU+XG4gICAgICAgIDxtYXhfcmVjZWl2ZV9tZXNzYWdlX3NpemU+LTE8L21heF9yZWNlaXZlX21lc3NhZ2Vfc2l6ZT5cblxuICAgICAgICA8IS0tIEVuYWJsZSBpZiB5b3Ugd2FudCB2ZXJ5IGRldGFpbGVkIGxvZ3MgLS0+XG4gICAgICAgIDx2ZXJib3NlX2xvZ3M+ZmFsc2U8L3ZlcmJvc2VfbG9ncz5cbiAgICA8L2dycGM+XG5cbiAgICA8IS0tIFVzZWQgd2l0aCBodHRwc19wb3J0IGFuZCB0Y3BfcG9ydF9zZWN1cmUuIEZ1bGwgc3NsIG9wdGlvbnMgbGlzdDpcbiAgICBodHRwczovL2dpdGh1Yi5jb20vQ2xpY2tIb3VzZS1FeHRyYXMvcG9jby9ibG9iL21hc3Rlci9OZXRTU0xfT3BlblNTTC9pbmNsdWRlL1BvY28vTmV0L1NTTE1hbmFnZXIuaCNMNzEgLS0+XG4gICAgPG9wZW5TU0w+XG4gICAgICAgIDxzZXJ2ZXI+IDwhLS0gVXNlZCBmb3IgaHR0cHMgc2VydmVyIEFORCBzZWN1cmUgdGNwIHBvcnQgLS0+XG4gICAgICAgICAgICA8IS0tIG9wZW5zc2wgcmVxIC1zdWJqIFwiL0NOPWxvY2FsaG9zdFwiIC1uZXcgLW5ld2tleSByc2E6MjA0OCAtZGF5cyAzNjUgLW5vZGVzIC14NTA5XG4gICAgICAgICAgICAta2V5b3V0IC9ldGMvY2xpY2tob3VzZS1zZXJ2ZXIvc2VydmVyLmtleSAtb3V0IC9ldGMvY2xpY2tob3VzZS1zZXJ2ZXIvc2VydmVyLmNydCAtLT5cbiAgICAgICAgICAgIDxjZXJ0aWZpY2F0ZUZpbGU+L2V0Yy9jbGlja2hvdXNlLXNlcnZlci9zZXJ2ZXIuY3J0PC9jZXJ0aWZpY2F0ZUZpbGU+XG4gICAgICAgICAgICA8cHJpdmF0ZUtleUZpbGU+L2V0Yy9jbGlja2hvdXNlLXNlcnZlci9zZXJ2ZXIua2V5PC9wcml2YXRlS2V5RmlsZT5cbiAgICAgICAgICAgIDwhLS0gZGhwYXJhbXMgYXJlIG9wdGlvbmFsLiBZb3UgY2FuIGRlbGV0ZSB0aGUgPGRoUGFyYW1zRmlsZT4gZWxlbWVudC5cbiAgICAgICAgICAgICAgICBUbyBnZW5lcmF0ZSBkaHBhcmFtcywgdXNlIHRoZSBmb2xsb3dpbmcgY29tbWFuZDpcbiAgICAgICAgICAgICAgICAgIG9wZW5zc2wgZGhwYXJhbSAtb3V0IC9ldGMvY2xpY2tob3VzZS1zZXJ2ZXIvZGhwYXJhbS5wZW0gNDA5NlxuICAgICAgICAgICAgICAgIE9ubHkgZmlsZSBmb3JtYXQgd2l0aCBCRUdJTiBESCBQQVJBTUVURVJTIGlzIHN1cHBvcnRlZC5cbiAgICAgICAgICAgICAgLS0+XG4gICAgICAgICAgICA8ZGhQYXJhbXNGaWxlPi9ldGMvY2xpY2tob3VzZS1zZXJ2ZXIvZGhwYXJhbS5wZW08L2RoUGFyYW1zRmlsZT5cbiAgICAgICAgICAgIDx2ZXJpZmljYXRpb25Nb2RlPm5vbmU8L3ZlcmlmaWNhdGlvbk1vZGU+XG4gICAgICAgICAgICA8bG9hZERlZmF1bHRDQUZpbGU+dHJ1ZTwvbG9hZERlZmF1bHRDQUZpbGU+XG4gICAgICAgICAgICA8Y2FjaGVTZXNzaW9ucz50cnVlPC9jYWNoZVNlc3Npb25zPlxuICAgICAgICAgICAgPGRpc2FibGVQcm90b2NvbHM+c3NsdjIsc3NsdjM8L2Rpc2FibGVQcm90b2NvbHM+XG4gICAgICAgICAgICA8cHJlZmVyU2VydmVyQ2lwaGVycz50cnVlPC9wcmVmZXJTZXJ2ZXJDaXBoZXJzPlxuICAgICAgICA8L3NlcnZlcj5cblxuICAgICAgICA8Y2xpZW50PiA8IS0tIFVzZWQgZm9yIGNvbm5lY3RpbmcgdG8gaHR0cHMgZGljdGlvbmFyeSBzb3VyY2UgYW5kIHNlY3VyZWQgWm9va2VlcGVyXG4gICAgICAgICAgICBjb21tdW5pY2F0aW9uIC0tPlxuICAgICAgICAgICAgPGxvYWREZWZhdWx0Q0FGaWxlPnRydWU8L2xvYWREZWZhdWx0Q0FGaWxlPlxuICAgICAgICAgICAgPGNhY2hlU2Vzc2lvbnM+dHJ1ZTwvY2FjaGVTZXNzaW9ucz5cbiAgICAgICAgICAgIDxkaXNhYmxlUHJvdG9jb2xzPnNzbHYyLHNzbHYzPC9kaXNhYmxlUHJvdG9jb2xzPlxuICAgICAgICAgICAgPHByZWZlclNlcnZlckNpcGhlcnM+dHJ1ZTwvcHJlZmVyU2VydmVyQ2lwaGVycz5cbiAgICAgICAgICAgIDwhLS0gVXNlIGZvciBzZWxmLXNpZ25lZDogPHZlcmlmaWNhdGlvbk1vZGU+bm9uZTwvdmVyaWZpY2F0aW9uTW9kZT4gLS0+XG4gICAgICAgICAgICA8aW52YWxpZENlcnRpZmljYXRlSGFuZGxlcj5cbiAgICAgICAgICAgICAgICA8IS0tIFVzZSBmb3Igc2VsZi1zaWduZWQ6IDxuYW1lPkFjY2VwdENlcnRpZmljYXRlSGFuZGxlcjwvbmFtZT4gLS0+XG4gICAgICAgICAgICAgICAgPG5hbWU+UmVqZWN0Q2VydGlmaWNhdGVIYW5kbGVyPC9uYW1lPlxuICAgICAgICAgICAgPC9pbnZhbGlkQ2VydGlmaWNhdGVIYW5kbGVyPlxuICAgICAgICA8L2NsaWVudD5cbiAgICA8L29wZW5TU0w+XG5cbiAgICA8IS0tIERlZmF1bHQgcm9vdCBwYWdlIG9uIGh0dHBbc10gc2VydmVyLiBGb3IgZXhhbXBsZSBsb2FkIFVJIGZyb20gaHR0cHM6Ly90YWJpeC5pby8gd2hlblxuICAgIG9wZW5pbmcgaHR0cDovL2xvY2FsaG9zdDo4MTIzIC0tPlxuICAgIDwhLS1cbiAgICA8aHR0cF9zZXJ2ZXJfZGVmYXVsdF9yZXNwb25zZT48IVtDREFUQVs8aHRtbCBuZy1hcHA9XCJTTUkyXCI+PGhlYWQ+PGJhc2VcbiAgICBocmVmPVwiaHR0cDovL3VpLnRhYml4LmlvL1wiPjwvaGVhZD48Ym9keT48ZGl2IHVpLXZpZXc9XCJcIiBjbGFzcz1cImNvbnRlbnQtdWlcIj48L2Rpdj48c2NyaXB0XG4gICAgc3JjPVwiaHR0cDovL2xvYWRlci50YWJpeC5pby9tYXN0ZXIuanNcIj48L3NjcmlwdD48L2JvZHk+PC9odG1sPl1dPjwvaHR0cF9zZXJ2ZXJfZGVmYXVsdF9yZXNwb25zZT5cbiAgICAtLT5cblxuICAgIDwhLS0gTWF4aW11bSBudW1iZXIgb2YgY29uY3VycmVudCBxdWVyaWVzLiAtLT5cbiAgICA8bWF4X2NvbmN1cnJlbnRfcXVlcmllcz4xMDA8L21heF9jb25jdXJyZW50X3F1ZXJpZXM+XG5cbiAgICA8IS0tIE1heGltdW0gbWVtb3J5IHVzYWdlIChyZXNpZGVudCBzZXQgc2l6ZSkgZm9yIHNlcnZlciBwcm9jZXNzLlxuICAgICAgICBaZXJvIHZhbHVlIG9yIHVuc2V0IG1lYW5zIGRlZmF1bHQuIERlZmF1bHQgaXMgXCJtYXhfc2VydmVyX21lbW9yeV91c2FnZV90b19yYW1fcmF0aW9cIiBvZiBhdmFpbGFibGVcbiAgICBwaHlzaWNhbCBSQU0uXG4gICAgICAgIElmIHRoZSB2YWx1ZSBpcyBsYXJnZXIgdGhhbiBcIm1heF9zZXJ2ZXJfbWVtb3J5X3VzYWdlX3RvX3JhbV9yYXRpb1wiIG9mIGF2YWlsYWJsZSBwaHlzaWNhbCBSQU0sIGl0XG4gICAgd2lsbCBiZSBjdXQgZG93bi5cblxuICAgICAgICBUaGUgY29uc3RyYWludCBpcyBjaGVja2VkIG9uIHF1ZXJ5IGV4ZWN1dGlvbiB0aW1lLlxuICAgICAgICBJZiBhIHF1ZXJ5IHRyaWVzIHRvIGFsbG9jYXRlIG1lbW9yeSBhbmQgdGhlIGN1cnJlbnQgbWVtb3J5IHVzYWdlIHBsdXMgYWxsb2NhdGlvbiBpcyBncmVhdGVyXG4gICAgICAgICAgdGhhbiBzcGVjaWZpZWQgdGhyZXNob2xkLCBleGNlcHRpb24gd2lsbCBiZSB0aHJvd24uXG5cbiAgICAgICAgSXQgaXMgbm90IHByYWN0aWNhbCB0byBzZXQgdGhpcyBjb25zdHJhaW50IHRvIHNtYWxsIHZhbHVlcyBsaWtlIGp1c3QgYSBmZXcgZ2lnYWJ5dGVzLFxuICAgICAgICAgIGJlY2F1c2UgbWVtb3J5IGFsbG9jYXRvciB3aWxsIGtlZXAgdGhpcyBhbW91bnQgb2YgbWVtb3J5IGluIGNhY2hlcyBhbmQgdGhlIHNlcnZlciB3aWxsIGRlbnkgc2VydmljZVxuICAgIG9mIHF1ZXJpZXMuXG4gICAgICAtLT5cbiAgICA8bWF4X3NlcnZlcl9tZW1vcnlfdXNhZ2U+MDwvbWF4X3NlcnZlcl9tZW1vcnlfdXNhZ2U+XG5cbiAgICA8IS0tIE1heGltdW0gbnVtYmVyIG9mIHRocmVhZHMgaW4gdGhlIEdsb2JhbCB0aHJlYWQgcG9vbC5cbiAgICBUaGlzIHdpbGwgZGVmYXVsdCB0byBhIG1heGltdW0gb2YgMTAwMDAgdGhyZWFkcyBpZiBub3Qgc3BlY2lmaWVkLlxuICAgIFRoaXMgc2V0dGluZyB3aWxsIGJlIHVzZWZ1bCBpbiBzY2VuYXJpb3Mgd2hlcmUgdGhlcmUgYXJlIGEgbGFyZ2UgbnVtYmVyXG4gICAgb2YgZGlzdHJpYnV0ZWQgcXVlcmllcyB0aGF0IGFyZSBydW5uaW5nIGNvbmN1cnJlbnRseSBidXQgYXJlIGlkbGluZyBtb3N0XG4gICAgb2YgdGhlIHRpbWUsIGluIHdoaWNoIGNhc2UgYSBoaWdoZXIgbnVtYmVyIG9mIHRocmVhZHMgbWlnaHQgYmUgcmVxdWlyZWQuXG4gICAgLS0+XG5cbiAgICA8bWF4X3RocmVhZF9wb29sX3NpemU+MTAwMDA8L21heF90aHJlYWRfcG9vbF9zaXplPlxuXG4gICAgPCEtLSBOdW1iZXIgb2Ygd29ya2VycyB0byByZWN5Y2xlIGNvbm5lY3Rpb25zIGluIGJhY2tncm91bmQgKHNlZSBhbHNvIGRyYWluX3RpbWVvdXQpLlxuICAgICAgICBJZiB0aGUgcG9vbCBpcyBmdWxsLCBjb25uZWN0aW9uIHdpbGwgYmUgZHJhaW5lZCBzeW5jaHJvbm91c2x5LiAtLT5cbiAgICA8IS0tIDxtYXhfdGhyZWFkc19mb3JfY29ubmVjdGlvbl9jb2xsZWN0b3I+MTA8L21heF90aHJlYWRzX2Zvcl9jb25uZWN0aW9uX2NvbGxlY3Rvcj4gLS0+XG5cbiAgICA8IS0tIE9uIG1lbW9yeSBjb25zdHJhaW5lZCBlbnZpcm9ubWVudHMgeW91IG1heSBoYXZlIHRvIHNldCB0aGlzIHRvIHZhbHVlIGxhcmdlciB0aGFuIDEuXG4gICAgICAtLT5cbiAgICA8bWF4X3NlcnZlcl9tZW1vcnlfdXNhZ2VfdG9fcmFtX3JhdGlvPjAuOTwvbWF4X3NlcnZlcl9tZW1vcnlfdXNhZ2VfdG9fcmFtX3JhdGlvPlxuXG4gICAgPCEtLSBTaW1wbGUgc2VydmVyLXdpZGUgbWVtb3J5IHByb2ZpbGVyLiBDb2xsZWN0IGEgc3RhY2sgdHJhY2UgYXQgZXZlcnkgcGVhayBhbGxvY2F0aW9uIHN0ZXAgKGluXG4gICAgYnl0ZXMpLlxuICAgICAgICBEYXRhIHdpbGwgYmUgc3RvcmVkIGluIHN5c3RlbS50cmFjZV9sb2cgdGFibGUgd2l0aCBxdWVyeV9pZCA9IGVtcHR5IHN0cmluZy5cbiAgICAgICAgWmVybyBtZWFucyBkaXNhYmxlZC5cbiAgICAgIC0tPlxuICAgIDx0b3RhbF9tZW1vcnlfcHJvZmlsZXJfc3RlcD40MTk0MzA0PC90b3RhbF9tZW1vcnlfcHJvZmlsZXJfc3RlcD5cblxuICAgIDwhLS0gQ29sbGVjdCByYW5kb20gYWxsb2NhdGlvbnMgYW5kIGRlYWxsb2NhdGlvbnMgYW5kIHdyaXRlIHRoZW0gaW50byBzeXN0ZW0udHJhY2VfbG9nIHdpdGhcbiAgICAnTWVtb3J5U2FtcGxlJyB0cmFjZV90eXBlLlxuICAgICAgICBUaGUgcHJvYmFiaWxpdHkgaXMgZm9yIGV2ZXJ5IGFsbG9jL2ZyZWUgcmVnYXJkbGVzcyB0byB0aGUgc2l6ZSBvZiB0aGUgYWxsb2NhdGlvbi5cbiAgICAgICAgTm90ZSB0aGF0IHNhbXBsaW5nIGhhcHBlbnMgb25seSB3aGVuIHRoZSBhbW91bnQgb2YgdW50cmFja2VkIG1lbW9yeSBleGNlZWRzIHRoZSB1bnRyYWNrZWQgbWVtb3J5XG4gICAgbGltaXQsXG4gICAgICAgICAgd2hpY2ggaXMgNCBNaUIgYnkgZGVmYXVsdCBidXQgY2FuIGJlIGxvd2VyZWQgaWYgJ3RvdGFsX21lbW9yeV9wcm9maWxlcl9zdGVwJyBpcyBsb3dlcmVkLlxuICAgICAgICBZb3UgbWF5IHdhbnQgdG8gc2V0ICd0b3RhbF9tZW1vcnlfcHJvZmlsZXJfc3RlcCcgdG8gMSBmb3IgZXh0cmEgZmluZSBncmFpbmVkIHNhbXBsaW5nLlxuICAgICAgLS0+XG4gICAgPHRvdGFsX21lbW9yeV90cmFja2VyX3NhbXBsZV9wcm9iYWJpbGl0eT4wPC90b3RhbF9tZW1vcnlfdHJhY2tlcl9zYW1wbGVfcHJvYmFiaWxpdHk+XG5cbiAgICA8IS0tIFNldCBsaW1pdCBvbiBudW1iZXIgb2Ygb3BlbiBmaWxlcyAoZGVmYXVsdDogbWF4aW11bSkuIFRoaXMgc2V0dGluZyBtYWtlcyBzZW5zZSBvbiBNYWMgT1MgWFxuICAgIGJlY2F1c2UgZ2V0cmxpbWl0KCkgZmFpbHMgdG8gcmV0cmlldmVcbiAgICAgICAgY29ycmVjdCBtYXhpbXVtIHZhbHVlLiAtLT5cbiAgICA8IS0tIDxtYXhfb3Blbl9maWxlcz4yNjIxNDQ8L21heF9vcGVuX2ZpbGVzPiAtLT5cblxuICAgIDwhLS0gU2l6ZSBvZiBjYWNoZSBvZiB1bmNvbXByZXNzZWQgYmxvY2tzIG9mIGRhdGEsIHVzZWQgaW4gdGFibGVzIG9mIE1lcmdlVHJlZSBmYW1pbHkuXG4gICAgICAgIEluIGJ5dGVzLiBDYWNoZSBpcyBzaW5nbGUgZm9yIHNlcnZlci4gTWVtb3J5IGlzIGFsbG9jYXRlZCBvbmx5IG9uIGRlbWFuZC5cbiAgICAgICAgQ2FjaGUgaXMgdXNlZCB3aGVuICd1c2VfdW5jb21wcmVzc2VkX2NhY2hlJyB1c2VyIHNldHRpbmcgdHVybmVkIG9uIChvZmYgYnkgZGVmYXVsdCkuXG4gICAgICAgIFVuY29tcHJlc3NlZCBjYWNoZSBpcyBhZHZhbnRhZ2VvdXMgb25seSBmb3IgdmVyeSBzaG9ydCBxdWVyaWVzIGFuZCBpbiByYXJlIGNhc2VzLlxuXG4gICAgICAgIE5vdGU6IHVuY29tcHJlc3NlZCBjYWNoZSBjYW4gYmUgcG9pbnRsZXNzIGZvciBsejQsIGJlY2F1c2UgbWVtb3J5IGJhbmR3aWR0aFxuICAgICAgICBpcyBzbG93ZXIgdGhhbiBtdWx0aS1jb3JlIGRlY29tcHJlc3Npb24gb24gc29tZSBzZXJ2ZXIgY29uZmlndXJhdGlvbnMuXG4gICAgICAgIEVuYWJsaW5nIGl0IGNhbiBzb21ldGltZXMgcGFyYWRveGljYWxseSBtYWtlIHF1ZXJpZXMgc2xvd2VyLlxuICAgICAgLS0+XG4gICAgPHVuY29tcHJlc3NlZF9jYWNoZV9zaXplPjg1ODk5MzQ1OTI8L3VuY29tcHJlc3NlZF9jYWNoZV9zaXplPlxuXG4gICAgPCEtLSBBcHByb3hpbWF0ZSBzaXplIG9mIG1hcmsgY2FjaGUsIHVzZWQgaW4gdGFibGVzIG9mIE1lcmdlVHJlZSBmYW1pbHkuXG4gICAgICAgIEluIGJ5dGVzLiBDYWNoZSBpcyBzaW5nbGUgZm9yIHNlcnZlci4gTWVtb3J5IGlzIGFsbG9jYXRlZCBvbmx5IG9uIGRlbWFuZC5cbiAgICAgICAgWW91IHNob3VsZCBub3QgbG93ZXIgdGhpcyB2YWx1ZS5cbiAgICAgIC0tPlxuICAgIDxtYXJrX2NhY2hlX3NpemU+NTM2ODcwOTEyMDwvbWFya19jYWNoZV9zaXplPlxuXG5cbiAgICA8IS0tIElmIHlvdSBlbmFibGUgdGhlIGBtaW5fYnl0ZXNfdG9fdXNlX21tYXBfaW9gIHNldHRpbmcsXG4gICAgICAgIHRoZSBkYXRhIGluIE1lcmdlVHJlZSB0YWJsZXMgY2FuIGJlIHJlYWQgd2l0aCBtbWFwIHRvIGF2b2lkIGNvcHlpbmcgZnJvbSBrZXJuZWwgdG8gdXNlcnNwYWNlLlxuICAgICAgICBJdCBtYWtlcyBzZW5zZSBvbmx5IGZvciBsYXJnZSBmaWxlcyBhbmQgaGVscHMgb25seSBpZiBkYXRhIHJlc2lkZSBpbiBwYWdlIGNhY2hlLlxuICAgICAgICBUbyBhdm9pZCBmcmVxdWVudCBvcGVuL21tYXAvbXVubWFwL2Nsb3NlIGNhbGxzICh3aGljaCBhcmUgdmVyeSBleHBlbnNpdmUgZHVlIHRvIGNvbnNlcXVlbnQgcGFnZVxuICAgIGZhdWx0cylcbiAgICAgICAgYW5kIHRvIHJldXNlIG1hcHBpbmdzIGZyb20gc2V2ZXJhbCB0aHJlYWRzIGFuZCBxdWVyaWVzLFxuICAgICAgICB0aGUgY2FjaGUgb2YgbWFwcGVkIGZpbGVzIGlzIG1haW50YWluZWQuIEl0cyBzaXplIGlzIHRoZSBudW1iZXIgb2YgbWFwcGVkIHJlZ2lvbnMgKHVzdWFsbHkgZXF1YWwgdG9cbiAgICB0aGUgbnVtYmVyIG9mIG1hcHBlZCBmaWxlcykuXG4gICAgICAgIFRoZSBhbW91bnQgb2YgZGF0YSBpbiBtYXBwZWQgZmlsZXMgY2FuIGJlIG1vbml0b3JlZFxuICAgICAgICBpbiBzeXN0ZW0ubWV0cmljcywgc3lzdGVtLm1ldHJpY19sb2cgYnkgdGhlIE1NYXBwZWRGaWxlcywgTU1hcHBlZEZpbGVCeXRlcyBtZXRyaWNzXG4gICAgICAgIGFuZCBpbiBzeXN0ZW0uYXN5bmNocm9ub3VzX21ldHJpY3MsIHN5c3RlbS5hc3luY2hyb25vdXNfbWV0cmljc19sb2cgYnkgdGhlIE1NYXBDYWNoZUNlbGxzIG1ldHJpYyxcbiAgICAgICAgYW5kIGFsc28gaW4gc3lzdGVtLmV2ZW50cywgc3lzdGVtLnByb2Nlc3Nlcywgc3lzdGVtLnF1ZXJ5X2xvZywgc3lzdGVtLnF1ZXJ5X3RocmVhZF9sb2csXG4gICAgc3lzdGVtLnF1ZXJ5X3ZpZXdzX2xvZyBieSB0aGVcbiAgICAgICAgQ3JlYXRlZFJlYWRCdWZmZXJNTWFwLCBDcmVhdGVkUmVhZEJ1ZmZlck1NYXBGYWlsZWQsIE1NYXBwZWRGaWxlQ2FjaGVIaXRzLCBNTWFwcGVkRmlsZUNhY2hlTWlzc2VzXG4gICAgZXZlbnRzLlxuICAgICAgICBOb3RlIHRoYXQgdGhlIGFtb3VudCBvZiBkYXRhIGluIG1hcHBlZCBmaWxlcyBkb2VzIG5vdCBjb25zdW1lIG1lbW9yeSBkaXJlY3RseSBhbmQgaXMgbm90IGFjY291bnRlZFxuICAgICAgICBpbiBxdWVyeSBvciBzZXJ2ZXIgbWVtb3J5IHVzYWdlIC0gYmVjYXVzZSB0aGlzIG1lbW9yeSBjYW4gYmUgZGlzY2FyZGVkIHNpbWlsYXIgdG8gT1MgcGFnZSBjYWNoZS5cbiAgICAgICAgVGhlIGNhY2hlIGlzIGRyb3BwZWQgKHRoZSBmaWxlcyBhcmUgY2xvc2VkKSBhdXRvbWF0aWNhbGx5IG9uIHJlbW92YWwgb2Ygb2xkIHBhcnRzIGluIE1lcmdlVHJlZSxcbiAgICAgICAgYWxzbyBpdCBjYW4gYmUgZHJvcHBlZCBtYW51YWxseSBieSB0aGUgU1lTVEVNIERST1AgTU1BUCBDQUNIRSBxdWVyeS5cbiAgICAgIC0tPlxuICAgIDxtbWFwX2NhY2hlX3NpemU+MTAwMDwvbW1hcF9jYWNoZV9zaXplPlxuXG4gICAgPCEtLSBDYWNoZSBzaXplIGluIGJ5dGVzIGZvciBjb21waWxlZCBleHByZXNzaW9ucy4tLT5cbiAgICA8Y29tcGlsZWRfZXhwcmVzc2lvbl9jYWNoZV9zaXplPjEzNDIxNzcyODwvY29tcGlsZWRfZXhwcmVzc2lvbl9jYWNoZV9zaXplPlxuXG4gICAgPCEtLSBDYWNoZSBzaXplIGluIGVsZW1lbnRzIGZvciBjb21waWxlZCBleHByZXNzaW9ucy4tLT5cbiAgICA8Y29tcGlsZWRfZXhwcmVzc2lvbl9jYWNoZV9lbGVtZW50c19zaXplPjEwMDAwPC9jb21waWxlZF9leHByZXNzaW9uX2NhY2hlX2VsZW1lbnRzX3NpemU+XG5cbiAgICA8IS0tIFBhdGggdG8gZGF0YSBkaXJlY3RvcnksIHdpdGggdHJhaWxpbmcgc2xhc2guIC0tPlxuICAgIDxwYXRoPi92YXIvbGliL2NsaWNraG91c2UvPC9wYXRoPlxuXG4gICAgPCEtLSBQYXRoIHRvIHRlbXBvcmFyeSBkYXRhIGZvciBwcm9jZXNzaW5nIGhhcmQgcXVlcmllcy4gLS0+XG4gICAgPHRtcF9wYXRoPi92YXIvbGliL2NsaWNraG91c2UvdG1wLzwvdG1wX3BhdGg+XG5cbiAgICA8IS0tIFBvbGljeSBmcm9tIHRoZSA8c3RvcmFnZV9jb25maWd1cmF0aW9uPiBmb3IgdGhlIHRlbXBvcmFyeSBmaWxlcy5cbiAgICAgICAgSWYgbm90IHNldCA8dG1wX3BhdGg+IGlzIHVzZWQsIG90aGVyd2lzZSA8dG1wX3BhdGg+IGlzIGlnbm9yZWQuXG5cbiAgICAgICAgTm90ZXM6XG4gICAgICAgIC0gbW92ZV9mYWN0b3IgICAgICAgICAgICAgIGlzIGlnbm9yZWRcbiAgICAgICAgLSBrZWVwX2ZyZWVfc3BhY2VfYnl0ZXMgICAgaXMgaWdub3JlZFxuICAgICAgICAtIG1heF9kYXRhX3BhcnRfc2l6ZV9ieXRlcyBpcyBpZ25vcmVkXG4gICAgICAgIC0geW91IG11c3QgaGF2ZSBleGFjdGx5IG9uZSB2b2x1bWUgaW4gdGhhdCBwb2xpY3lcbiAgICAtLT5cbiAgICA8IS0tIDx0bXBfcG9saWN5PnRtcDwvdG1wX3BvbGljeT4gLS0+XG5cbiAgICA8IS0tIERpcmVjdG9yeSB3aXRoIHVzZXIgcHJvdmlkZWQgZmlsZXMgdGhhdCBhcmUgYWNjZXNzaWJsZSBieSAnZmlsZScgdGFibGUgZnVuY3Rpb24uIC0tPlxuICAgIDx1c2VyX2ZpbGVzX3BhdGg+L3Zhci9saWIvY2xpY2tob3VzZS91c2VyX2ZpbGVzLzwvdXNlcl9maWxlc19wYXRoPlxuXG4gICAgPCEtLSBMREFQIHNlcnZlciBkZWZpbml0aW9ucy4gLS0+XG4gICAgPGxkYXBfc2VydmVycz5cbiAgICAgICAgPCEtLSBMaXN0IExEQVAgc2VydmVycyB3aXRoIHRoZWlyIGNvbm5lY3Rpb24gcGFyYW1ldGVycyBoZXJlIHRvIGxhdGVyIDEpIHVzZSB0aGVtIGFzXG4gICAgICAgIGF1dGhlbnRpY2F0b3JzIGZvciBkZWRpY2F0ZWQgbG9jYWwgdXNlcnMsXG4gICAgICAgICAgICAgIHdobyBoYXZlICdsZGFwJyBhdXRoZW50aWNhdGlvbiBtZWNoYW5pc20gc3BlY2lmaWVkIGluc3RlYWQgb2YgJ3Bhc3N3b3JkJywgb3IgdG8gMikgdXNlIHRoZW0gYXNcbiAgICAgICAgcmVtb3RlIHVzZXIgZGlyZWN0b3JpZXMuXG4gICAgICAgICAgICBQYXJhbWV0ZXJzOlxuICAgICAgICAgICAgICAgIGhvc3QgLSBMREFQIHNlcnZlciBob3N0bmFtZSBvciBJUCwgdGhpcyBwYXJhbWV0ZXIgaXMgbWFuZGF0b3J5IGFuZCBjYW5ub3QgYmUgZW1wdHkuXG4gICAgICAgICAgICAgICAgcG9ydCAtIExEQVAgc2VydmVyIHBvcnQsIGRlZmF1bHQgaXMgNjM2IGlmIGVuYWJsZV90bHMgaXMgc2V0IHRvIHRydWUsIDM4OSBvdGhlcndpc2UuXG4gICAgICAgICAgICAgICAgYmluZF9kbiAtIHRlbXBsYXRlIHVzZWQgdG8gY29uc3RydWN0IHRoZSBETiB0byBiaW5kIHRvLlxuICAgICAgICAgICAgICAgICAgICAgICAgVGhlIHJlc3VsdGluZyBETiB3aWxsIGJlIGNvbnN0cnVjdGVkIGJ5IHJlcGxhY2luZyBhbGwgJ3t1c2VyX25hbWV9JyBzdWJzdHJpbmdzIG9mIHRoZSB0ZW1wbGF0ZSB3aXRoXG4gICAgICAgIHRoZSBhY3R1YWxcbiAgICAgICAgICAgICAgICAgICAgICAgIHVzZXIgbmFtZSBkdXJpbmcgZWFjaCBhdXRoZW50aWNhdGlvbiBhdHRlbXB0LlxuICAgICAgICAgICAgICAgIHVzZXJfZG5fZGV0ZWN0aW9uIC0gc2VjdGlvbiB3aXRoIExEQVAgc2VhcmNoIHBhcmFtZXRlcnMgZm9yIGRldGVjdGluZyB0aGUgYWN0dWFsIHVzZXIgRE4gb2YgdGhlXG4gICAgICAgIGJvdW5kIHVzZXIuXG4gICAgICAgICAgICAgICAgICAgICAgICBUaGlzIGlzIG1haW5seSB1c2VkIGluIHNlYXJjaCBmaWx0ZXJzIGZvciBmdXJ0aGVyIHJvbGUgbWFwcGluZyB3aGVuIHRoZSBzZXJ2ZXIgaXMgQWN0aXZlIERpcmVjdG9yeS5cbiAgICAgICAgVGhlXG4gICAgICAgICAgICAgICAgICAgICAgICByZXN1bHRpbmcgdXNlciBETiB3aWxsIGJlIHVzZWQgd2hlbiByZXBsYWNpbmcgJ3t1c2VyX2RufScgc3Vic3RyaW5ncyB3aGVyZXZlciB0aGV5IGFyZSBhbGxvd2VkLiBCeVxuICAgICAgICBkZWZhdWx0LFxuICAgICAgICAgICAgICAgICAgICAgICAgdXNlciBETiBpcyBzZXQgZXF1YWwgdG8gYmluZCBETiwgYnV0IG9uY2Ugc2VhcmNoIGlzIHBlcmZvcm1lZCwgaXQgd2lsbCBiZSB1cGRhdGVkIHdpdGggdG8gdGhlXG4gICAgICAgIGFjdHVhbCBkZXRlY3RlZFxuICAgICAgICAgICAgICAgICAgICAgICAgdXNlciBETiB2YWx1ZS5cbiAgICAgICAgICAgICAgICAgICAgYmFzZV9kbiAtIHRlbXBsYXRlIHVzZWQgdG8gY29uc3RydWN0IHRoZSBiYXNlIEROIGZvciB0aGUgTERBUCBzZWFyY2guXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVGhlIHJlc3VsdGluZyBETiB3aWxsIGJlIGNvbnN0cnVjdGVkIGJ5IHJlcGxhY2luZyBhbGwgJ3t1c2VyX25hbWV9JyBhbmQgJ3tiaW5kX2RufScgc3Vic3RyaW5nc1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9mIHRoZSB0ZW1wbGF0ZSB3aXRoIHRoZSBhY3R1YWwgdXNlciBuYW1lIGFuZCBiaW5kIEROIGR1cmluZyB0aGUgTERBUCBzZWFyY2guXG4gICAgICAgICAgICAgICAgICAgIHNjb3BlIC0gc2NvcGUgb2YgdGhlIExEQVAgc2VhcmNoLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFjY2VwdGVkIHZhbHVlcyBhcmU6ICdiYXNlJywgJ29uZV9sZXZlbCcsICdjaGlsZHJlbicsICdzdWJ0cmVlJyAodGhlIGRlZmF1bHQpLlxuICAgICAgICAgICAgICAgICAgICBzZWFyY2hfZmlsdGVyIC0gdGVtcGxhdGUgdXNlZCB0byBjb25zdHJ1Y3QgdGhlIHNlYXJjaCBmaWx0ZXIgZm9yIHRoZSBMREFQIHNlYXJjaC5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUaGUgcmVzdWx0aW5nIGZpbHRlciB3aWxsIGJlIGNvbnN0cnVjdGVkIGJ5IHJlcGxhY2luZyBhbGwgJ3t1c2VyX25hbWV9JywgJ3tiaW5kX2RufScsIGFuZFxuICAgICAgICAne2Jhc2VfZG59J1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnN0cmluZ3Mgb2YgdGhlIHRlbXBsYXRlIHdpdGggdGhlIGFjdHVhbCB1c2VyIG5hbWUsIGJpbmQgRE4sIGFuZCBiYXNlIEROIGR1cmluZyB0aGUgTERBUCBzZWFyY2guXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgTm90ZSwgdGhhdCB0aGUgc3BlY2lhbCBjaGFyYWN0ZXJzIG11c3QgYmUgZXNjYXBlZCBwcm9wZXJseSBpbiBYTUwuXG4gICAgICAgICAgICAgICAgdmVyaWZpY2F0aW9uX2Nvb2xkb3duIC0gYSBwZXJpb2Qgb2YgdGltZSwgaW4gc2Vjb25kcywgYWZ0ZXIgYSBzdWNjZXNzZnVsIGJpbmQgYXR0ZW1wdCwgZHVyaW5nIHdoaWNoXG4gICAgICAgIGEgdXNlciB3aWxsIGJlIGFzc3VtZWRcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvIGJlIHN1Y2Nlc3NmdWxseSBhdXRoZW50aWNhdGVkIGZvciBhbGwgY29uc2VjdXRpdmUgcmVxdWVzdHMgd2l0aG91dCBjb250YWN0aW5nIHRoZSBMREFQIHNlcnZlci5cbiAgICAgICAgICAgICAgICAgICAgICAgIFNwZWNpZnkgMCAodGhlIGRlZmF1bHQpIHRvIGRpc2FibGUgY2FjaGluZyBhbmQgZm9yY2UgY29udGFjdGluZyB0aGUgTERBUCBzZXJ2ZXIgZm9yIGVhY2hcbiAgICAgICAgYXV0aGVudGljYXRpb24gcmVxdWVzdC5cbiAgICAgICAgICAgICAgICBlbmFibGVfdGxzIC0gZmxhZyB0byB0cmlnZ2VyIHVzZSBvZiBzZWN1cmUgY29ubmVjdGlvbiB0byB0aGUgTERBUCBzZXJ2ZXIuXG4gICAgICAgICAgICAgICAgICAgICAgICBTcGVjaWZ5ICdubycgZm9yIHBsYWluIHRleHQgKGxkYXA6Ly8pIHByb3RvY29sIChub3QgcmVjb21tZW5kZWQpLlxuICAgICAgICAgICAgICAgICAgICAgICAgU3BlY2lmeSAneWVzJyBmb3IgTERBUCBvdmVyIFNTTC9UTFMgKGxkYXBzOi8vKSBwcm90b2NvbCAocmVjb21tZW5kZWQsIHRoZSBkZWZhdWx0KS5cbiAgICAgICAgICAgICAgICAgICAgICAgIFNwZWNpZnkgJ3N0YXJ0dGxzJyBmb3IgbGVnYWN5IFN0YXJ0VExTIHByb3RvY29sIChwbGFpbiB0ZXh0IChsZGFwOi8vKSBwcm90b2NvbCwgdXBncmFkZWQgdG8gVExTKS5cbiAgICAgICAgICAgICAgICB0bHNfbWluaW11bV9wcm90b2NvbF92ZXJzaW9uIC0gdGhlIG1pbmltdW0gcHJvdG9jb2wgdmVyc2lvbiBvZiBTU0wvVExTLlxuICAgICAgICAgICAgICAgICAgICAgICAgQWNjZXB0ZWQgdmFsdWVzIGFyZTogJ3NzbDInLCAnc3NsMycsICd0bHMxLjAnLCAndGxzMS4xJywgJ3RsczEuMicgKHRoZSBkZWZhdWx0KS5cbiAgICAgICAgICAgICAgICB0bHNfcmVxdWlyZV9jZXJ0IC0gU1NML1RMUyBwZWVyIGNlcnRpZmljYXRlIHZlcmlmaWNhdGlvbiBiZWhhdmlvci5cbiAgICAgICAgICAgICAgICAgICAgICAgIEFjY2VwdGVkIHZhbHVlcyBhcmU6ICduZXZlcicsICdhbGxvdycsICd0cnknLCAnZGVtYW5kJyAodGhlIGRlZmF1bHQpLlxuICAgICAgICAgICAgICAgIHRsc19jZXJ0X2ZpbGUgLSBwYXRoIHRvIGNlcnRpZmljYXRlIGZpbGUuXG4gICAgICAgICAgICAgICAgdGxzX2tleV9maWxlIC0gcGF0aCB0byBjZXJ0aWZpY2F0ZSBrZXkgZmlsZS5cbiAgICAgICAgICAgICAgICB0bHNfY2FfY2VydF9maWxlIC0gcGF0aCB0byBDQSBjZXJ0aWZpY2F0ZSBmaWxlLlxuICAgICAgICAgICAgICAgIHRsc19jYV9jZXJ0X2RpciAtIHBhdGggdG8gdGhlIGRpcmVjdG9yeSBjb250YWluaW5nIENBIGNlcnRpZmljYXRlcy5cbiAgICAgICAgICAgICAgICB0bHNfY2lwaGVyX3N1aXRlIC0gYWxsb3dlZCBjaXBoZXIgc3VpdGUgKGluIE9wZW5TU0wgbm90YXRpb24pLlxuICAgICAgICAgICAgRXhhbXBsZTpcbiAgICAgICAgICAgICAgICA8bXlfbGRhcF9zZXJ2ZXI+XG4gICAgICAgICAgICAgICAgICAgIDxob3N0PmxvY2FsaG9zdDwvaG9zdD5cbiAgICAgICAgICAgICAgICAgICAgPHBvcnQ+NjM2PC9wb3J0PlxuICAgICAgICAgICAgICAgICAgICA8YmluZF9kbj51aWQ9e3VzZXJfbmFtZX0sb3U9dXNlcnMsZGM9ZXhhbXBsZSxkYz1jb208L2JpbmRfZG4+XG4gICAgICAgICAgICAgICAgICAgIDx2ZXJpZmljYXRpb25fY29vbGRvd24+MzAwPC92ZXJpZmljYXRpb25fY29vbGRvd24+XG4gICAgICAgICAgICAgICAgICAgIDxlbmFibGVfdGxzPnllczwvZW5hYmxlX3Rscz5cbiAgICAgICAgICAgICAgICAgICAgPHRsc19taW5pbXVtX3Byb3RvY29sX3ZlcnNpb24+dGxzMS4yPC90bHNfbWluaW11bV9wcm90b2NvbF92ZXJzaW9uPlxuICAgICAgICAgICAgICAgICAgICA8dGxzX3JlcXVpcmVfY2VydD5kZW1hbmQ8L3Rsc19yZXF1aXJlX2NlcnQ+XG4gICAgICAgICAgICAgICAgICAgIDx0bHNfY2VydF9maWxlPi9wYXRoL3RvL3Rsc19jZXJ0X2ZpbGU8L3Rsc19jZXJ0X2ZpbGU+XG4gICAgICAgICAgICAgICAgICAgIDx0bHNfa2V5X2ZpbGU+L3BhdGgvdG8vdGxzX2tleV9maWxlPC90bHNfa2V5X2ZpbGU+XG4gICAgICAgICAgICAgICAgICAgIDx0bHNfY2FfY2VydF9maWxlPi9wYXRoL3RvL3Rsc19jYV9jZXJ0X2ZpbGU8L3Rsc19jYV9jZXJ0X2ZpbGU+XG4gICAgICAgICAgICAgICAgICAgIDx0bHNfY2FfY2VydF9kaXI+L3BhdGgvdG8vdGxzX2NhX2NlcnRfZGlyPC90bHNfY2FfY2VydF9kaXI+XG4gICAgICAgIDx0bHNfY2lwaGVyX3N1aXRlPkVDREhFLUVDRFNBLUFFUzI1Ni1HQ00tU0hBMzg0OkVDREhFLVJTQS1BRVMyNTYtR0NNLVNIQTM4NDpBRVMyNTYtR0NNLVNIQTM4NDwvdGxzX2NpcGhlcl9zdWl0ZT5cbiAgICAgICAgICAgICAgICA8L215X2xkYXBfc2VydmVyPlxuICAgICAgICAgICAgRXhhbXBsZSAodHlwaWNhbCBBY3RpdmUgRGlyZWN0b3J5IHdpdGggY29uZmlndXJlZCB1c2VyIEROIGRldGVjdGlvbiBmb3IgZnVydGhlciByb2xlIG1hcHBpbmcpOlxuICAgICAgICAgICAgICAgIDxteV9hZF9zZXJ2ZXI+XG4gICAgICAgICAgICAgICAgICAgIDxob3N0PmxvY2FsaG9zdDwvaG9zdD5cbiAgICAgICAgICAgICAgICAgICAgPHBvcnQ+Mzg5PC9wb3J0PlxuICAgICAgICAgICAgICAgICAgICA8YmluZF9kbj5FWEFNUExFXFx7dXNlcl9uYW1lfTwvYmluZF9kbj5cbiAgICAgICAgICAgICAgICAgICAgPHVzZXJfZG5fZGV0ZWN0aW9uPlxuICAgICAgICAgICAgICAgICAgICAgICAgPGJhc2VfZG4+Q049VXNlcnMsREM9ZXhhbXBsZSxEQz1jb208L2Jhc2VfZG4+XG4gICAgICAgICAgICAgICAgICAgICAgICA8c2VhcmNoX2ZpbHRlcj4oJmFtcDsob2JqZWN0Q2xhc3M9dXNlcikoc0FNQWNjb3VudE5hbWU9e3VzZXJfbmFtZX0pKTwvc2VhcmNoX2ZpbHRlcj5cbiAgICAgICAgICAgICAgICAgICAgPC91c2VyX2RuX2RldGVjdGlvbj5cbiAgICAgICAgICAgICAgICAgICAgPGVuYWJsZV90bHM+bm88L2VuYWJsZV90bHM+XG4gICAgICAgICAgICAgICAgPC9teV9hZF9zZXJ2ZXI+XG4gICAgICAgIC0tPlxuICAgIDwvbGRhcF9zZXJ2ZXJzPlxuXG4gICAgPCEtLSBUbyBlbmFibGUgS2VyYmVyb3MgYXV0aGVudGljYXRpb24gc3VwcG9ydCBmb3IgSFRUUCByZXF1ZXN0cyAoR1NTLVNQTkVHTyksIGZvciB0aG9zZSB1c2Vyc1xuICAgIHdobyBhcmUgZXhwbGljaXRseSBjb25maWd1cmVkXG4gICAgICAgICAgdG8gYXV0aGVudGljYXRlIHZpYSBLZXJiZXJvcywgZGVmaW5lIGEgc2luZ2xlICdrZXJiZXJvcycgc2VjdGlvbiBoZXJlLlxuICAgICAgICBQYXJhbWV0ZXJzOlxuICAgICAgICAgICAgcHJpbmNpcGFsIC0gY2Fub25pY2FsIHNlcnZpY2UgcHJpbmNpcGFsIG5hbWUsIHRoYXQgd2lsbCBiZSBhY3F1aXJlZCBhbmQgdXNlZCB3aGVuIGFjY2VwdGluZ1xuICAgIHNlY3VyaXR5IGNvbnRleHRzLlxuICAgICAgICAgICAgICAgICAgICBUaGlzIHBhcmFtZXRlciBpcyBvcHRpb25hbCwgaWYgb21pdHRlZCwgdGhlIGRlZmF1bHQgcHJpbmNpcGFsIHdpbGwgYmUgdXNlZC5cbiAgICAgICAgICAgICAgICAgICAgVGhpcyBwYXJhbWV0ZXIgY2Fubm90IGJlIHNwZWNpZmllZCB0b2dldGhlciB3aXRoICdyZWFsbScgcGFyYW1ldGVyLlxuICAgICAgICAgICAgcmVhbG0gLSBhIHJlYWxtLCB0aGF0IHdpbGwgYmUgdXNlZCB0byByZXN0cmljdCBhdXRoZW50aWNhdGlvbiB0byBvbmx5IHRob3NlIHJlcXVlc3RzIHdob3NlXG4gICAgaW5pdGlhdG9yJ3MgcmVhbG0gbWF0Y2hlcyBpdC5cbiAgICAgICAgICAgICAgICAgICAgVGhpcyBwYXJhbWV0ZXIgaXMgb3B0aW9uYWwsIGlmIG9taXR0ZWQsIG5vIGFkZGl0aW9uYWwgZmlsdGVyaW5nIGJ5IHJlYWxtIHdpbGwgYmUgYXBwbGllZC5cbiAgICAgICAgICAgICAgICAgICAgVGhpcyBwYXJhbWV0ZXIgY2Fubm90IGJlIHNwZWNpZmllZCB0b2dldGhlciB3aXRoICdwcmluY2lwYWwnIHBhcmFtZXRlci5cbiAgICAgICAgRXhhbXBsZTpcbiAgICAgICAgICAgIDxrZXJiZXJvcyAvPlxuICAgICAgICBFeGFtcGxlOlxuICAgICAgICAgICAgPGtlcmJlcm9zPlxuICAgICAgICAgICAgICAgIDxwcmluY2lwYWw+SFRUUC9jbGlja2hvdXNlLmV4YW1wbGUuY29tQEVYQU1QTEUuQ09NPC9wcmluY2lwYWw+XG4gICAgICAgICAgICA8L2tlcmJlcm9zPlxuICAgICAgICBFeGFtcGxlOlxuICAgICAgICAgICAgPGtlcmJlcm9zPlxuICAgICAgICAgICAgICAgIDxyZWFsbT5FWEFNUExFLkNPTTwvcmVhbG0+XG4gICAgICAgICAgICA8L2tlcmJlcm9zPlxuICAgIC0tPlxuXG4gICAgPCEtLSBTb3VyY2VzIHRvIHJlYWQgdXNlcnMsIHJvbGVzLCBhY2Nlc3MgcmlnaHRzLCBwcm9maWxlcyBvZiBzZXR0aW5ncywgcXVvdGFzLiAtLT5cbiAgICA8dXNlcl9kaXJlY3Rvcmllcz5cbiAgICAgICAgPHVzZXJzX3htbD5cbiAgICAgICAgICAgIDwhLS0gUGF0aCB0byBjb25maWd1cmF0aW9uIGZpbGUgd2l0aCBwcmVkZWZpbmVkIHVzZXJzLiAtLT5cbiAgICAgICAgICAgIDxwYXRoPnVzZXJzLnhtbDwvcGF0aD5cbiAgICAgICAgPC91c2Vyc194bWw+XG4gICAgICAgIDxsb2NhbF9kaXJlY3Rvcnk+XG4gICAgICAgICAgICA8IS0tIFBhdGggdG8gZm9sZGVyIHdoZXJlIHVzZXJzIGNyZWF0ZWQgYnkgU1FMIGNvbW1hbmRzIGFyZSBzdG9yZWQuIC0tPlxuICAgICAgICAgICAgPHBhdGg+L3Zhci9saWIvY2xpY2tob3VzZS9hY2Nlc3MvPC9wYXRoPlxuICAgICAgICA8L2xvY2FsX2RpcmVjdG9yeT5cblxuICAgICAgICA8IS0tIFRvIGFkZCBhbiBMREFQIHNlcnZlciBhcyBhIHJlbW90ZSB1c2VyIGRpcmVjdG9yeSBvZiB1c2VycyB0aGF0IGFyZSBub3QgZGVmaW5lZCBsb2NhbGx5LFxuICAgICAgICBkZWZpbmUgYSBzaW5nbGUgJ2xkYXAnIHNlY3Rpb25cbiAgICAgICAgICAgICAgd2l0aCB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnM6XG4gICAgICAgICAgICAgICAgc2VydmVyIC0gb25lIG9mIExEQVAgc2VydmVyIG5hbWVzIGRlZmluZWQgaW4gJ2xkYXBfc2VydmVycycgY29uZmlnIHNlY3Rpb24gYWJvdmUuXG4gICAgICAgICAgICAgICAgICAgICAgICBUaGlzIHBhcmFtZXRlciBpcyBtYW5kYXRvcnkgYW5kIGNhbm5vdCBiZSBlbXB0eS5cbiAgICAgICAgICAgICAgICByb2xlcyAtIHNlY3Rpb24gd2l0aCBhIGxpc3Qgb2YgbG9jYWxseSBkZWZpbmVkIHJvbGVzIHRoYXQgd2lsbCBiZSBhc3NpZ25lZCB0byBlYWNoIHVzZXIgcmV0cmlldmVkXG4gICAgICAgIGZyb20gdGhlIExEQVAgc2VydmVyLlxuICAgICAgICAgICAgICAgICAgICAgICAgSWYgbm8gcm9sZXMgYXJlIHNwZWNpZmllZCBoZXJlIG9yIGFzc2lnbmVkIGR1cmluZyByb2xlIG1hcHBpbmcgKGJlbG93KSwgdXNlciB3aWxsIG5vdCBiZSBhYmxlIHRvXG4gICAgICAgIHBlcmZvcm0gYW55XG4gICAgICAgICAgICAgICAgICAgICAgICBhY3Rpb25zIGFmdGVyIGF1dGhlbnRpY2F0aW9uLlxuICAgICAgICAgICAgICAgIHJvbGVfbWFwcGluZyAtIHNlY3Rpb24gd2l0aCBMREFQIHNlYXJjaCBwYXJhbWV0ZXJzIGFuZCBtYXBwaW5nIHJ1bGVzLlxuICAgICAgICAgICAgICAgICAgICAgICAgV2hlbiBhIHVzZXIgYXV0aGVudGljYXRlcywgd2hpbGUgc3RpbGwgYm91bmQgdG8gTERBUCwgYW4gTERBUCBzZWFyY2ggaXMgcGVyZm9ybWVkIHVzaW5nXG4gICAgICAgIHNlYXJjaF9maWx0ZXIgYW5kIHRoZVxuICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSBvZiB0aGUgbG9nZ2VkIGluIHVzZXIuIEZvciBlYWNoIGVudHJ5IGZvdW5kIGR1cmluZyB0aGF0IHNlYXJjaCwgdGhlIHZhbHVlIG9mIHRoZSBzcGVjaWZpZWRcbiAgICAgICAgYXR0cmlidXRlIGlzXG4gICAgICAgICAgICAgICAgICAgICAgICBleHRyYWN0ZWQuIEZvciBlYWNoIGF0dHJpYnV0ZSB2YWx1ZSB0aGF0IGhhcyB0aGUgc3BlY2lmaWVkIHByZWZpeCwgdGhlIHByZWZpeCBpcyByZW1vdmVkLCBhbmQgdGhlXG4gICAgICAgIHJlc3Qgb2YgdGhlXG4gICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSBiZWNvbWVzIHRoZSBuYW1lIG9mIGEgbG9jYWwgcm9sZSBkZWZpbmVkIGluIENsaWNrSG91c2UsIHdoaWNoIGlzIGV4cGVjdGVkIHRvIGJlIGNyZWF0ZWRcbiAgICAgICAgYmVmb3JlaGFuZCBieVxuICAgICAgICAgICAgICAgICAgICAgICAgQ1JFQVRFIFJPTEUgY29tbWFuZC5cbiAgICAgICAgICAgICAgICAgICAgICAgIFRoZXJlIGNhbiBiZSBtdWx0aXBsZSAncm9sZV9tYXBwaW5nJyBzZWN0aW9ucyBkZWZpbmVkIGluc2lkZSB0aGUgc2FtZSAnbGRhcCcgc2VjdGlvbi4gQWxsIG9mIHRoZW1cbiAgICAgICAgd2lsbCBiZVxuICAgICAgICAgICAgICAgICAgICAgICAgYXBwbGllZC5cbiAgICAgICAgICAgICAgICAgICAgYmFzZV9kbiAtIHRlbXBsYXRlIHVzZWQgdG8gY29uc3RydWN0IHRoZSBiYXNlIEROIGZvciB0aGUgTERBUCBzZWFyY2guXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVGhlIHJlc3VsdGluZyBETiB3aWxsIGJlIGNvbnN0cnVjdGVkIGJ5IHJlcGxhY2luZyBhbGwgJ3t1c2VyX25hbWV9JywgJ3tiaW5kX2RufScsIGFuZCAne3VzZXJfZG59J1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnN0cmluZ3Mgb2YgdGhlIHRlbXBsYXRlIHdpdGggdGhlIGFjdHVhbCB1c2VyIG5hbWUsIGJpbmQgRE4sIGFuZCB1c2VyIEROIGR1cmluZyBlYWNoIExEQVAgc2VhcmNoLlxuICAgICAgICAgICAgICAgICAgICBzY29wZSAtIHNjb3BlIG9mIHRoZSBMREFQIHNlYXJjaC5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBBY2NlcHRlZCB2YWx1ZXMgYXJlOiAnYmFzZScsICdvbmVfbGV2ZWwnLCAnY2hpbGRyZW4nLCAnc3VidHJlZScgKHRoZSBkZWZhdWx0KS5cbiAgICAgICAgICAgICAgICAgICAgc2VhcmNoX2ZpbHRlciAtIHRlbXBsYXRlIHVzZWQgdG8gY29uc3RydWN0IHRoZSBzZWFyY2ggZmlsdGVyIGZvciB0aGUgTERBUCBzZWFyY2guXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVGhlIHJlc3VsdGluZyBmaWx0ZXIgd2lsbCBiZSBjb25zdHJ1Y3RlZCBieSByZXBsYWNpbmcgYWxsICd7dXNlcl9uYW1lfScsICd7YmluZF9kbn0nLCAne3VzZXJfZG59JyxcbiAgICAgICAgYW5kXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3tiYXNlX2RufScgc3Vic3RyaW5ncyBvZiB0aGUgdGVtcGxhdGUgd2l0aCB0aGUgYWN0dWFsIHVzZXIgbmFtZSwgYmluZCBETiwgdXNlciBETiwgYW5kIGJhc2UgRE5cbiAgICAgICAgZHVyaW5nXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZWFjaCBMREFQIHNlYXJjaC5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBOb3RlLCB0aGF0IHRoZSBzcGVjaWFsIGNoYXJhY3RlcnMgbXVzdCBiZSBlc2NhcGVkIHByb3Blcmx5IGluIFhNTC5cbiAgICAgICAgICAgICAgICAgICAgYXR0cmlidXRlIC0gYXR0cmlidXRlIG5hbWUgd2hvc2UgdmFsdWVzIHdpbGwgYmUgcmV0dXJuZWQgYnkgdGhlIExEQVAgc2VhcmNoLiAnY24nLCBieSBkZWZhdWx0LlxuICAgICAgICAgICAgICAgICAgICBwcmVmaXggLSBwcmVmaXgsIHRoYXQgd2lsbCBiZSBleHBlY3RlZCB0byBiZSBpbiBmcm9udCBvZiBlYWNoIHN0cmluZyBpbiB0aGUgb3JpZ2luYWwgbGlzdCBvZlxuICAgICAgICBzdHJpbmdzIHJldHVybmVkIGJ5XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlIExEQVAgc2VhcmNoLiBQcmVmaXggd2lsbCBiZSByZW1vdmVkIGZyb20gdGhlIG9yaWdpbmFsIHN0cmluZ3MgYW5kIHJlc3VsdGluZyBzdHJpbmdzIHdpbGwgYmVcbiAgICAgICAgdHJlYXRlZFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzIGxvY2FsIHJvbGUgbmFtZXMuIEVtcHR5LCBieSBkZWZhdWx0LlxuICAgICAgICAgICAgRXhhbXBsZTpcbiAgICAgICAgICAgICAgICA8bGRhcD5cbiAgICAgICAgICAgICAgICAgICAgPHNlcnZlcj5teV9sZGFwX3NlcnZlcjwvc2VydmVyPlxuICAgICAgICAgICAgICAgICAgICA8cm9sZXM+XG4gICAgICAgICAgICAgICAgICAgICAgICA8bXlfbG9jYWxfcm9sZTEgLz5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxteV9sb2NhbF9yb2xlMiAvPlxuICAgICAgICAgICAgICAgICAgICA8L3JvbGVzPlxuICAgICAgICAgICAgICAgICAgICA8cm9sZV9tYXBwaW5nPlxuICAgICAgICAgICAgICAgICAgICAgICAgPGJhc2VfZG4+b3U9Z3JvdXBzLGRjPWV4YW1wbGUsZGM9Y29tPC9iYXNlX2RuPlxuICAgICAgICAgICAgICAgICAgICAgICAgPHNjb3BlPnN1YnRyZWU8L3Njb3BlPlxuICAgICAgICAgICAgICAgICAgICAgICAgPHNlYXJjaF9maWx0ZXI+KCZhbXA7KG9iamVjdENsYXNzPWdyb3VwT2ZOYW1lcykobWVtYmVyPXtiaW5kX2RufSkpPC9zZWFyY2hfZmlsdGVyPlxuICAgICAgICAgICAgICAgICAgICAgICAgPGF0dHJpYnV0ZT5jbjwvYXR0cmlidXRlPlxuICAgICAgICAgICAgICAgICAgICAgICAgPHByZWZpeD5jbGlja2hvdXNlXzwvcHJlZml4PlxuICAgICAgICAgICAgICAgICAgICA8L3JvbGVfbWFwcGluZz5cbiAgICAgICAgICAgICAgICA8L2xkYXA+XG4gICAgICAgICAgICBFeGFtcGxlICh0eXBpY2FsIEFjdGl2ZSBEaXJlY3Rvcnkgd2l0aCByb2xlIG1hcHBpbmcgdGhhdCByZWxpZXMgb24gdGhlIGRldGVjdGVkIHVzZXIgRE4pOlxuICAgICAgICAgICAgICAgIDxsZGFwPlxuICAgICAgICAgICAgICAgICAgICA8c2VydmVyPm15X2FkX3NlcnZlcjwvc2VydmVyPlxuICAgICAgICAgICAgICAgICAgICA8cm9sZV9tYXBwaW5nPlxuICAgICAgICAgICAgICAgICAgICAgICAgPGJhc2VfZG4+Q049VXNlcnMsREM9ZXhhbXBsZSxEQz1jb208L2Jhc2VfZG4+XG4gICAgICAgICAgICAgICAgICAgICAgICA8YXR0cmlidXRlPkNOPC9hdHRyaWJ1dGU+XG4gICAgICAgICAgICAgICAgICAgICAgICA8c2NvcGU+c3VidHJlZTwvc2NvcGU+XG4gICAgICAgICAgICAgICAgICAgICAgICA8c2VhcmNoX2ZpbHRlcj4oJmFtcDsob2JqZWN0Q2xhc3M9Z3JvdXApKG1lbWJlcj17dXNlcl9kbn0pKTwvc2VhcmNoX2ZpbHRlcj5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxwcmVmaXg+Y2xpY2tob3VzZV88L3ByZWZpeD5cbiAgICAgICAgICAgICAgICAgICAgPC9yb2xlX21hcHBpbmc+XG4gICAgICAgICAgICAgICAgPC9sZGFwPlxuICAgICAgICAtLT5cbiAgICA8L3VzZXJfZGlyZWN0b3JpZXM+XG5cbiAgICA8IS0tIERlZmF1bHQgcHJvZmlsZSBvZiBzZXR0aW5ncy4gLS0+XG4gICAgPGRlZmF1bHRfcHJvZmlsZT5kZWZhdWx0PC9kZWZhdWx0X3Byb2ZpbGU+XG5cbiAgICA8IS0tIENvbW1hLXNlcGFyYXRlZCBsaXN0IG9mIHByZWZpeGVzIGZvciB1c2VyLWRlZmluZWQgc2V0dGluZ3MuIC0tPlxuICAgIDxjdXN0b21fc2V0dGluZ3NfcHJlZml4ZXM+PC9jdXN0b21fc2V0dGluZ3NfcHJlZml4ZXM+XG5cbiAgICA8IS0tIFN5c3RlbSBwcm9maWxlIG9mIHNldHRpbmdzLiBUaGlzIHNldHRpbmdzIGFyZSB1c2VkIGJ5IGludGVybmFsIHByb2Nlc3NlcyAoRGlzdHJpYnV0ZWQgRERMXG4gICAgd29ya2VyIGFuZCBzbyBvbikuIC0tPlxuICAgIDwhLS0gPHN5c3RlbV9wcm9maWxlPmRlZmF1bHQ8L3N5c3RlbV9wcm9maWxlPiAtLT5cblxuICAgIDwhLS0gQnVmZmVyIHByb2ZpbGUgb2Ygc2V0dGluZ3MuXG4gICAgICAgIFRoaXMgc2V0dGluZ3MgYXJlIHVzZWQgYnkgQnVmZmVyIHN0b3JhZ2UgdG8gZmx1c2ggZGF0YSB0byB0aGUgdW5kZXJseWluZyB0YWJsZS5cbiAgICAgICAgRGVmYXVsdDogdXNlZCBmcm9tIHN5c3RlbV9wcm9maWxlIGRpcmVjdGl2ZS5cbiAgICAtLT5cbiAgICA8IS0tIDxidWZmZXJfcHJvZmlsZT5kZWZhdWx0PC9idWZmZXJfcHJvZmlsZT4gLS0+XG5cbiAgICA8IS0tIERlZmF1bHQgZGF0YWJhc2UuIC0tPlxuICAgIDxkZWZhdWx0X2RhdGFiYXNlPmRlZmF1bHQ8L2RlZmF1bHRfZGF0YWJhc2U+XG5cbiAgICA8IS0tIFNlcnZlciB0aW1lIHpvbmUgY291bGQgYmUgc2V0IGhlcmUuXG5cbiAgICAgICAgVGltZSB6b25lIGlzIHVzZWQgd2hlbiBjb252ZXJ0aW5nIGJldHdlZW4gU3RyaW5nIGFuZCBEYXRlVGltZSB0eXBlcyxcbiAgICAgICAgICB3aGVuIHByaW50aW5nIERhdGVUaW1lIGluIHRleHQgZm9ybWF0cyBhbmQgcGFyc2luZyBEYXRlVGltZSBmcm9tIHRleHQsXG4gICAgICAgICAgaXQgaXMgdXNlZCBpbiBkYXRlIGFuZCB0aW1lIHJlbGF0ZWQgZnVuY3Rpb25zLCBpZiBzcGVjaWZpYyB0aW1lIHpvbmUgd2FzIG5vdCBwYXNzZWQgYXMgYW4gYXJndW1lbnQuXG5cbiAgICAgICAgVGltZSB6b25lIGlzIHNwZWNpZmllZCBhcyBpZGVudGlmaWVyIGZyb20gSUFOQSB0aW1lIHpvbmUgZGF0YWJhc2UsIGxpa2UgVVRDIG9yIEFmcmljYS9BYmlkamFuLlxuICAgICAgICBJZiBub3Qgc3BlY2lmaWVkLCBzeXN0ZW0gdGltZSB6b25lIGF0IHNlcnZlciBzdGFydHVwIGlzIHVzZWQuXG5cbiAgICAgICAgUGxlYXNlIG5vdGUsIHRoYXQgc2VydmVyIGNvdWxkIGRpc3BsYXkgdGltZSB6b25lIGFsaWFzIGluc3RlYWQgb2Ygc3BlY2lmaWVkIG5hbWUuXG4gICAgICAgIEV4YW1wbGU6IFctU1UgaXMgYW4gYWxpYXMgZm9yIEV1cm9wZS9Nb3Njb3cgYW5kIFp1bHUgaXMgYW4gYWxpYXMgZm9yIFVUQy5cbiAgICAtLT5cbiAgICA8IS0tIDx0aW1lem9uZT5FdXJvcGUvTW9zY293PC90aW1lem9uZT4gLS0+XG5cbiAgICA8IS0tIFlvdSBjYW4gc3BlY2lmeSB1bWFzayBoZXJlIChzZWUgXCJtYW4gdW1hc2tcIikuIFNlcnZlciB3aWxsIGFwcGx5IGl0IG9uIHN0YXJ0dXAuXG4gICAgICAgIE51bWJlciBpcyBhbHdheXMgcGFyc2VkIGFzIG9jdGFsLiBEZWZhdWx0IHVtYXNrIGlzIDAyNyAob3RoZXIgdXNlcnMgY2Fubm90IHJlYWQgbG9ncywgZGF0YSBmaWxlcyxcbiAgICBldGM7IGdyb3VwIGNhbiBvbmx5IHJlYWQpLlxuICAgIC0tPlxuICAgIDwhLS0gPHVtYXNrPjAyMjwvdW1hc2s+IC0tPlxuXG4gICAgPCEtLSBQZXJmb3JtIG1sb2NrYWxsIGFmdGVyIHN0YXJ0dXAgdG8gbG93ZXIgZmlyc3QgcXVlcmllcyBsYXRlbmN5XG4gICAgICAgICAgYW5kIHRvIHByZXZlbnQgY2xpY2tob3VzZSBleGVjdXRhYmxlIGZyb20gYmVpbmcgcGFnZWQgb3V0IHVuZGVyIGhpZ2ggSU8gbG9hZC5cbiAgICAgICAgRW5hYmxpbmcgdGhpcyBvcHRpb24gaXMgcmVjb21tZW5kZWQgYnV0IHdpbGwgbGVhZCB0byBpbmNyZWFzZWQgc3RhcnR1cCB0aW1lIGZvciB1cCB0byBhIGZld1xuICAgIHNlY29uZHMuXG4gICAgLS0+XG4gICAgPG1sb2NrX2V4ZWN1dGFibGU+dHJ1ZTwvbWxvY2tfZXhlY3V0YWJsZT5cblxuICAgIDwhLS0gUmVhbGxvY2F0ZSBtZW1vcnkgZm9yIG1hY2hpbmUgY29kZSAoXCJ0ZXh0XCIpIHVzaW5nIGh1Z2UgcGFnZXMuIEhpZ2hseSBleHBlcmltZW50YWwuIC0tPlxuICAgIDxyZW1hcF9leGVjdXRhYmxlPmZhbHNlPC9yZW1hcF9leGVjdXRhYmxlPlxuXG4gICAgPCFbQ0RBVEFbXG4gICAgICAgIFVuY29tbWVudCBiZWxvdyBpbiBvcmRlciB0byB1c2UgSkRCQyB0YWJsZSBlbmdpbmUgYW5kIGZ1bmN0aW9uLlxuXG4gICAgICAgIFRvIGluc3RhbGwgYW5kIHJ1biBKREJDIGJyaWRnZSBpbiBiYWNrZ3JvdW5kOlxuICAgICAgICAqIFtEZWJpYW4vVWJ1bnR1XVxuICAgICAgICAgIGV4cG9ydCBNVk5fVVJMPWh0dHBzOi8vcmVwbzEubWF2ZW4ub3JnL21hdmVuMi9ydS95YW5kZXgvY2xpY2tob3VzZS9jbGlja2hvdXNlLWpkYmMtYnJpZGdlXG4gICAgICAgICAgZXhwb3J0IFBLR19WRVI9JChjdXJsIC1zTCAkTVZOX1VSTC9tYXZlbi1tZXRhZGF0YS54bWwgfCBncmVwICc8cmVsZWFzZT4nIHwgc2VkIC1lICdzfC4qPlxcKC4qXFwpPC4qfFxcMXwnKVxuICAgICAgICAgIHdnZXQgaHR0cHM6Ly9naXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1qZGJjLWJyaWRnZS9yZWxlYXNlcy9kb3dubG9hZC92JFBLR19WRVIvY2xpY2tob3VzZS1qZGJjLWJyaWRnZV8kUEtHX1ZFUi0xX2FsbC5kZWJcbiAgICAgICAgICBhcHQgaW5zdGFsbCAtLW5vLWluc3RhbGwtcmVjb21tZW5kcyAtZiAuL2NsaWNraG91c2UtamRiYy1icmlkZ2VfJFBLR19WRVItMV9hbGwuZGViXG4gICAgICAgICAgY2xpY2tob3VzZS1qZGJjLWJyaWRnZSAmXG5cbiAgICAgICAgKiBbQ2VudE9TL1JIRUxdXG4gICAgICAgICAgZXhwb3J0IE1WTl9VUkw9aHR0cHM6Ly9yZXBvMS5tYXZlbi5vcmcvbWF2ZW4yL3J1L3lhbmRleC9jbGlja2hvdXNlL2NsaWNraG91c2UtamRiYy1icmlkZ2VcbiAgICAgICAgICBleHBvcnQgUEtHX1ZFUj0kKGN1cmwgLXNMICRNVk5fVVJML21hdmVuLW1ldGFkYXRhLnhtbCB8IGdyZXAgJzxyZWxlYXNlPicgfCBzZWQgLWUgJ3N8Lio+XFwoLipcXCk8Lip8XFwxfCcpXG4gICAgICAgICAgd2dldCBodHRwczovL2dpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWpkYmMtYnJpZGdlL3JlbGVhc2VzL2Rvd25sb2FkL3YkUEtHX1ZFUi9jbGlja2hvdXNlLWpkYmMtYnJpZGdlLSRQS0dfVkVSLTEubm9hcmNoLnJwbVxuICAgICAgICAgIHl1bSBsb2NhbGluc3RhbGwgLXkgY2xpY2tob3VzZS1qZGJjLWJyaWRnZS0kUEtHX1ZFUi0xLm5vYXJjaC5ycG1cbiAgICAgICAgICBjbGlja2hvdXNlLWpkYmMtYnJpZGdlICZcblxuICAgICAgICBQbGVhc2UgcmVmZXIgdG8gaHR0cHM6Ly9naXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1qZGJjLWJyaWRnZSN1c2FnZSBmb3IgbW9yZSBpbmZvcm1hdGlvbi5cbiAgICBdXT5cbiAgICA8IS0tXG4gICAgPGpkYmNfYnJpZGdlPlxuICAgICAgICA8aG9zdD4xMjcuMC4wLjE8L2hvc3Q+XG4gICAgICAgIDxwb3J0PjkwMTk8L3BvcnQ+XG4gICAgPC9qZGJjX2JyaWRnZT5cbiAgICAtLT5cblxuICAgIDwhLS0gQ29uZmlndXJhdGlvbiBvZiBjbHVzdGVycyB0aGF0IGNvdWxkIGJlIHVzZWQgaW4gRGlzdHJpYnV0ZWQgdGFibGVzLlxuICAgICAgICBodHRwczovL2NsaWNraG91c2UuY29tL2RvY3MvZW4vb3BlcmF0aW9ucy90YWJsZV9lbmdpbmVzL2Rpc3RyaWJ1dGVkL1xuICAgICAgLS0+XG4gICAgPHJlbW90ZV9zZXJ2ZXJzPlxuXG4gICAgICAgIDwhLS0gVGVzdCBvbmx5IHNoYXJkIGNvbmZpZyBmb3IgdGVzdGluZyBkaXN0cmlidXRlZCBzdG9yYWdlIC0tPlxuICAgICAgICA8cG9zdGhvZz5cbiAgICAgICAgICAgIDwhLS0gSW50ZXItc2VydmVyIHBlci1jbHVzdGVyIHNlY3JldCBmb3IgRGlzdHJpYnV0ZWQgcXVlcmllc1xuICAgICAgICAgICAgICAgIGRlZmF1bHQ6IG5vIHNlY3JldCAobm8gYXV0aGVudGljYXRpb24gd2lsbCBiZSBwZXJmb3JtZWQpXG5cbiAgICAgICAgICAgICAgICBJZiBzZXQsIHRoZW4gRGlzdHJpYnV0ZWQgcXVlcmllcyB3aWxsIGJlIHZhbGlkYXRlZCBvbiBzaGFyZHMsIHNvIGF0IGxlYXN0OlxuICAgICAgICAgICAgICAgIC0gc3VjaCBjbHVzdGVyIHNob3VsZCBleGlzdCBvbiB0aGUgc2hhcmQsXG4gICAgICAgICAgICAgICAgLSBzdWNoIGNsdXN0ZXIgc2hvdWxkIGhhdmUgdGhlIHNhbWUgc2VjcmV0LlxuXG4gICAgICAgICAgICAgICAgQW5kIGFsc28gKGFuZCB3aGljaCBpcyBtb3JlIGltcG9ydGFudCksIHRoZSBpbml0aWFsX3VzZXIgd2lsbFxuICAgICAgICAgICAgICAgIGJlIHVzZWQgYXMgY3VycmVudCB1c2VyIGZvciB0aGUgcXVlcnkuXG5cbiAgICAgICAgICAgICAgICBSaWdodCBub3cgdGhlIHByb3RvY29sIGlzIHByZXR0eSBzaW1wbGUgYW5kIGl0IG9ubHkgdGFrZXMgaW50byBhY2NvdW50OlxuICAgICAgICAgICAgICAgIC0gY2x1c3RlciBuYW1lXG4gICAgICAgICAgICAgICAgLSBxdWVyeVxuXG4gICAgICAgICAgICAgICAgQWxzbyBpdCB3aWxsIGJlIG5pY2UgaWYgdGhlIGZvbGxvd2luZyB3aWxsIGJlIGltcGxlbWVudGVkOlxuICAgICAgICAgICAgICAgIC0gc291cmNlIGhvc3RuYW1lIChzZWUgaW50ZXJzZXJ2ZXJfaHR0cF9ob3N0KSwgYnV0IHRoZW4gaXQgd2lsbCBkZXBlbmRzIGZyb20gRE5TLFxuICAgICAgICAgICAgICAgICAgaXQgY2FuIHVzZSBJUCBhZGRyZXNzIGluc3RlYWQsIGJ1dCB0aGVuIHRoZSB5b3UgbmVlZCB0byBnZXQgY29ycmVjdCBvbiB0aGUgaW5pdGlhdG9yIG5vZGUuXG4gICAgICAgICAgICAgICAgLSB0YXJnZXQgaG9zdG5hbWUgLyBpcCBhZGRyZXNzIChzYW1lIG5vdGVzIGFzIGZvciBzb3VyY2UgaG9zdG5hbWUpXG4gICAgICAgICAgICAgICAgLSB0aW1lLWJhc2VkIHNlY3VyaXR5IHRva2Vuc1xuICAgICAgICAgICAgLS0+XG4gICAgICAgICAgICA8IS0tIDxzZWNyZXQ+PC9zZWNyZXQ+IC0tPlxuXG4gICAgICAgICAgICA8c2hhcmQ+XG4gICAgICAgICAgICAgICAgPCEtLSBPcHRpb25hbC4gV2hldGhlciB0byB3cml0ZSBkYXRhIHRvIGp1c3Qgb25lIG9mIHRoZSByZXBsaWNhcy4gRGVmYXVsdDogZmFsc2VcbiAgICAgICAgICAgICAgICAod3JpdGUgZGF0YSB0byBhbGwgcmVwbGljYXMpLiAtLT5cbiAgICAgICAgICAgICAgICA8IS0tIDxpbnRlcm5hbF9yZXBsaWNhdGlvbj5mYWxzZTwvaW50ZXJuYWxfcmVwbGljYXRpb24+IC0tPlxuICAgICAgICAgICAgICAgIDwhLS0gT3B0aW9uYWwuIFNoYXJkIHdlaWdodCB3aGVuIHdyaXRpbmcgZGF0YS4gRGVmYXVsdDogMS4gLS0+XG4gICAgICAgICAgICAgICAgPCEtLSA8d2VpZ2h0PjE8L3dlaWdodD4gLS0+XG4gICAgICAgICAgICAgICAgPHJlcGxpY2E+XG4gICAgICAgICAgICAgICAgICAgIDxob3N0PmxvY2FsaG9zdDwvaG9zdD5cbiAgICAgICAgICAgICAgICAgICAgPHBvcnQ+OTAwMDwvcG9ydD5cbiAgICAgICAgICAgICAgICAgICAgPCEtLSBPcHRpb25hbC4gUHJpb3JpdHkgb2YgdGhlIHJlcGxpY2EgZm9yIGxvYWRfYmFsYW5jaW5nLiBEZWZhdWx0OiAxIChsZXNzXG4gICAgICAgICAgICAgICAgICAgIHZhbHVlIGhhcyBtb3JlIHByaW9yaXR5KS4gLS0+XG4gICAgICAgICAgICAgICAgICAgIDwhLS0gPHByaW9yaXR5PjE8L3ByaW9yaXR5PiAtLT5cbiAgICAgICAgICAgICAgICA8L3JlcGxpY2E+XG4gICAgICAgICAgICA8L3NoYXJkPlxuICAgICAgICA8L3Bvc3Rob2c+XG4gICAgPC9yZW1vdGVfc2VydmVycz5cblxuICAgIDwhLS0gVGhlIGxpc3Qgb2YgaG9zdHMgYWxsb3dlZCB0byB1c2UgaW4gVVJMLXJlbGF0ZWQgc3RvcmFnZSBlbmdpbmVzIGFuZCB0YWJsZSBmdW5jdGlvbnMuXG4gICAgICAgIElmIHRoaXMgc2VjdGlvbiBpcyBub3QgcHJlc2VudCBpbiBjb25maWd1cmF0aW9uLCBhbGwgaG9zdHMgYXJlIGFsbG93ZWQuXG4gICAgLS0+XG4gICAgPHJlbW90ZV91cmxfYWxsb3dfaG9zdHM+XG4gICAgICAgIDwhLS0gSG9zdCBzaG91bGQgYmUgc3BlY2lmaWVkIGV4YWN0bHkgYXMgaW4gVVJMLiBUaGUgbmFtZSBpcyBjaGVja2VkIGJlZm9yZSBETlMgcmVzb2x1dGlvbi5cbiAgICAgICAgICAgIEV4YW1wbGU6IFwieWFuZGV4LnJ1XCIsIFwieWFuZGV4LnJ1LlwiIGFuZCBcInd3dy55YW5kZXgucnVcIiBhcmUgZGlmZmVyZW50IGhvc3RzLlxuICAgICAgICAgICAgICAgICAgICBJZiBwb3J0IGlzIGV4cGxpY2l0bHkgc3BlY2lmaWVkIGluIFVSTCwgdGhlIGhvc3Q6cG9ydCBpcyBjaGVja2VkIGFzIGEgd2hvbGUuXG4gICAgICAgICAgICAgICAgICAgIElmIGhvc3Qgc3BlY2lmaWVkIGhlcmUgd2l0aG91dCBwb3J0LCBhbnkgcG9ydCB3aXRoIHRoaXMgaG9zdCBhbGxvd2VkLlxuICAgICAgICAgICAgICAgICAgICBcInlhbmRleC5ydVwiIC0+IFwieWFuZGV4LnJ1OjQ0M1wiLCBcInlhbmRleC5ydTo4MFwiIGV0Yy4gaXMgYWxsb3dlZCwgYnV0IFwieWFuZGV4LnJ1OjgwXCIgLT4gb25seVxuICAgICAgICBcInlhbmRleC5ydTo4MFwiIGlzIGFsbG93ZWQuXG4gICAgICAgICAgICBJZiB0aGUgaG9zdCBpcyBzcGVjaWZpZWQgYXMgSVAgYWRkcmVzcywgaXQgaXMgY2hlY2tlZCBhcyBzcGVjaWZpZWQgaW4gVVJMLiBFeGFtcGxlOlxuICAgICAgICBcIlsyYTAyOjZiODphOjphXVwiLlxuICAgICAgICAgICAgSWYgdGhlcmUgYXJlIHJlZGlyZWN0cyBhbmQgc3VwcG9ydCBmb3IgcmVkaXJlY3RzIGlzIGVuYWJsZWQsIGV2ZXJ5IHJlZGlyZWN0ICh0aGUgTG9jYXRpb24gZmllbGQpIGlzXG4gICAgICAgIGNoZWNrZWQuXG4gICAgICAgICAgICBIb3N0IHNob3VsZCBiZSBzcGVjaWZpZWQgdXNpbmcgdGhlIGhvc3QgeG1sIHRhZzpcbiAgICAgICAgICAgICAgICAgICAgPGhvc3Q+eWFuZGV4LnJ1PC9ob3N0PlxuICAgICAgICAtLT5cblxuICAgICAgICA8IS0tIFJlZ3VsYXIgZXhwcmVzc2lvbiBjYW4gYmUgc3BlY2lmaWVkLiBSRTIgZW5naW5lIGlzIHVzZWQgZm9yIHJlZ2V4cHMuXG4gICAgICAgICAgICBSZWdleHBzIGFyZSBub3QgYWxpZ25lZDogZG9uJ3QgZm9yZ2V0IHRvIGFkZCBeIGFuZCAkLiBBbHNvIGRvbid0IGZvcmdldCB0byBlc2NhcGUgZG90ICguKVxuICAgICAgICBtZXRhY2hhcmFjdGVyXG4gICAgICAgICAgICAoZm9yZ2V0dGluZyB0byBkbyBzbyBpcyBhIGNvbW1vbiBzb3VyY2Ugb2YgZXJyb3IpLlxuICAgICAgICAtLT5cbiAgICAgICAgPGhvc3RfcmVnZXhwPi4qPC9ob3N0X3JlZ2V4cD5cbiAgICA8L3JlbW90ZV91cmxfYWxsb3dfaG9zdHM+XG5cbiAgICA8IS0tIElmIGVsZW1lbnQgaGFzICdpbmNsJyBhdHRyaWJ1dGUsIHRoZW4gZm9yIGl0J3MgdmFsdWUgd2lsbCBiZSB1c2VkIGNvcnJlc3BvbmRpbmdcbiAgICBzdWJzdGl0dXRpb24gZnJvbSBhbm90aGVyIGZpbGUuXG4gICAgICAgIEJ5IGRlZmF1bHQsIHBhdGggdG8gZmlsZSB3aXRoIHN1YnN0aXR1dGlvbnMgaXMgL2V0Yy9tZXRyaWthLnhtbC4gSXQgY291bGQgYmUgY2hhbmdlZCBpbiBjb25maWcgaW5cbiAgICAnaW5jbHVkZV9mcm9tJyBlbGVtZW50LlxuICAgICAgICBWYWx1ZXMgZm9yIHN1YnN0aXR1dGlvbnMgYXJlIHNwZWNpZmllZCBpbiAvY2xpY2tob3VzZS9uYW1lX29mX3N1YnN0aXR1dGlvbiBlbGVtZW50cyBpbiB0aGF0IGZpbGUuXG4gICAgICAtLT5cblxuICAgIDwhLS0gWm9vS2VlcGVyIGlzIHVzZWQgdG8gc3RvcmUgbWV0YWRhdGEgYWJvdXQgcmVwbGljYXMsIHdoZW4gdXNpbmcgUmVwbGljYXRlZCB0YWJsZXMuXG4gICAgICAgIE9wdGlvbmFsLiBJZiB5b3UgZG9uJ3QgdXNlIHJlcGxpY2F0ZWQgdGFibGVzLCB5b3UgY291bGQgb21pdCB0aGF0LlxuXG4gICAgICAgIFNlZSBodHRwczovL2NsaWNraG91c2UuY29tL2RvY3MvZW4vZW5naW5lcy90YWJsZS1lbmdpbmVzL21lcmdldHJlZS1mYW1pbHkvcmVwbGljYXRpb24vXG4gICAgICAtLT5cblxuICAgIDx6b29rZWVwZXI+XG4gICAgICAgIDxub2RlPlxuICAgICAgICAgICAgPGhvc3Q+em9va2VlcGVyPC9ob3N0PlxuICAgICAgICAgICAgPHBvcnQ+MjE4MTwvcG9ydD5cbiAgICAgICAgPC9ub2RlPlxuICAgIDwvem9va2VlcGVyPlxuXG4gICAgPCEtLSBTdWJzdGl0dXRpb25zIGZvciBwYXJhbWV0ZXJzIG9mIHJlcGxpY2F0ZWQgdGFibGVzLlxuICAgICAgICAgIE9wdGlvbmFsLiBJZiB5b3UgZG9uJ3QgdXNlIHJlcGxpY2F0ZWQgdGFibGVzLCB5b3UgY291bGQgb21pdCB0aGF0LlxuXG4gICAgICAgIFNlZVxuICAgIGh0dHBzOi8vY2xpY2tob3VzZS5jb20vZG9jcy9lbi9lbmdpbmVzL3RhYmxlLWVuZ2luZXMvbWVyZ2V0cmVlLWZhbWlseS9yZXBsaWNhdGlvbi8jY3JlYXRpbmctcmVwbGljYXRlZC10YWJsZXNcbiAgICAgIC0tPlxuXG4gICAgPG1hY3Jvcz5cbiAgICAgICAgPHNoYXJkPjAxPC9zaGFyZD5cbiAgICAgICAgPHJlcGxpY2E+Y2gxPC9yZXBsaWNhPlxuICAgIDwvbWFjcm9zPlxuXG5cbiAgICA8IS0tIFJlbG9hZGluZyBpbnRlcnZhbCBmb3IgZW1iZWRkZWQgZGljdGlvbmFyaWVzLCBpbiBzZWNvbmRzLiBEZWZhdWx0OiAzNjAwLiAtLT5cbiAgICA8YnVpbHRpbl9kaWN0aW9uYXJpZXNfcmVsb2FkX2ludGVydmFsPjM2MDA8L2J1aWx0aW5fZGljdGlvbmFyaWVzX3JlbG9hZF9pbnRlcnZhbD5cblxuXG4gICAgPCEtLSBNYXhpbXVtIHNlc3Npb24gdGltZW91dCwgaW4gc2Vjb25kcy4gRGVmYXVsdDogMzYwMC4gLS0+XG4gICAgPG1heF9zZXNzaW9uX3RpbWVvdXQ+MzYwMDwvbWF4X3Nlc3Npb25fdGltZW91dD5cblxuICAgIDwhLS0gRGVmYXVsdCBzZXNzaW9uIHRpbWVvdXQsIGluIHNlY29uZHMuIERlZmF1bHQ6IDYwLiAtLT5cbiAgICA8ZGVmYXVsdF9zZXNzaW9uX3RpbWVvdXQ+NjA8L2RlZmF1bHRfc2Vzc2lvbl90aW1lb3V0PlxuXG4gICAgPCEtLSBTZW5kaW5nIGRhdGEgdG8gR3JhcGhpdGUgZm9yIG1vbml0b3JpbmcuIFNldmVyYWwgc2VjdGlvbnMgY2FuIGJlIGRlZmluZWQuIC0tPlxuICAgIDwhLS1cbiAgICAgICAgaW50ZXJ2YWwgLSBzZW5kIGV2ZXJ5IFggc2Vjb25kXG4gICAgICAgIHJvb3RfcGF0aCAtIHByZWZpeCBmb3Iga2V5c1xuICAgICAgICBob3N0bmFtZV9pbl9wYXRoIC0gYXBwZW5kIGhvc3RuYW1lIHRvIHJvb3RfcGF0aCAoZGVmYXVsdCA9IHRydWUpXG4gICAgICAgIG1ldHJpY3MgLSBzZW5kIGRhdGEgZnJvbSB0YWJsZSBzeXN0ZW0ubWV0cmljc1xuICAgICAgICBldmVudHMgLSBzZW5kIGRhdGEgZnJvbSB0YWJsZSBzeXN0ZW0uZXZlbnRzXG4gICAgICAgIGFzeW5jaHJvbm91c19tZXRyaWNzIC0gc2VuZCBkYXRhIGZyb20gdGFibGUgc3lzdGVtLmFzeW5jaHJvbm91c19tZXRyaWNzXG4gICAgLS0+XG4gICAgPCEtLVxuICAgIDxncmFwaGl0ZT5cbiAgICAgICAgPGhvc3Q+bG9jYWxob3N0PC9ob3N0PlxuICAgICAgICA8cG9ydD40MjAwMDwvcG9ydD5cbiAgICAgICAgPHRpbWVvdXQ+MC4xPC90aW1lb3V0PlxuICAgICAgICA8aW50ZXJ2YWw+NjA8L2ludGVydmFsPlxuICAgICAgICA8cm9vdF9wYXRoPm9uZV9taW48L3Jvb3RfcGF0aD5cbiAgICAgICAgPGhvc3RuYW1lX2luX3BhdGg+dHJ1ZTwvaG9zdG5hbWVfaW5fcGF0aD5cblxuICAgICAgICA8bWV0cmljcz50cnVlPC9tZXRyaWNzPlxuICAgICAgICA8ZXZlbnRzPnRydWU8L2V2ZW50cz5cbiAgICAgICAgPGV2ZW50c19jdW11bGF0aXZlPmZhbHNlPC9ldmVudHNfY3VtdWxhdGl2ZT5cbiAgICAgICAgPGFzeW5jaHJvbm91c19tZXRyaWNzPnRydWU8L2FzeW5jaHJvbm91c19tZXRyaWNzPlxuICAgIDwvZ3JhcGhpdGU+XG4gICAgPGdyYXBoaXRlPlxuICAgICAgICA8aG9zdD5sb2NhbGhvc3Q8L2hvc3Q+XG4gICAgICAgIDxwb3J0PjQyMDAwPC9wb3J0PlxuICAgICAgICA8dGltZW91dD4wLjE8L3RpbWVvdXQ+XG4gICAgICAgIDxpbnRlcnZhbD4xPC9pbnRlcnZhbD5cbiAgICAgICAgPHJvb3RfcGF0aD5vbmVfc2VjPC9yb290X3BhdGg+XG5cbiAgICAgICAgPG1ldHJpY3M+dHJ1ZTwvbWV0cmljcz5cbiAgICAgICAgPGV2ZW50cz50cnVlPC9ldmVudHM+XG4gICAgICAgIDxldmVudHNfY3VtdWxhdGl2ZT5mYWxzZTwvZXZlbnRzX2N1bXVsYXRpdmU+XG4gICAgICAgIDxhc3luY2hyb25vdXNfbWV0cmljcz5mYWxzZTwvYXN5bmNocm9ub3VzX21ldHJpY3M+XG4gICAgPC9ncmFwaGl0ZT5cbiAgICAtLT5cblxuICAgIDwhLS0gU2VydmUgZW5kcG9pbnQgZm9yIFByb21ldGhldXMgbW9uaXRvcmluZy4gLS0+XG4gICAgPCEtLVxuICAgICAgICBlbmRwb2ludCAtIG1lcnRpY3MgcGF0aCAocmVsYXRpdmUgdG8gcm9vdCwgc3RhdHJpbmcgd2l0aCBcIi9cIilcbiAgICAgICAgcG9ydCAtIHBvcnQgdG8gc2V0dXAgc2VydmVyLiBJZiBub3QgZGVmaW5lZCBvciAwIHRoYW4gaHR0cF9wb3J0IHVzZWRcbiAgICAgICAgbWV0cmljcyAtIHNlbmQgZGF0YSBmcm9tIHRhYmxlIHN5c3RlbS5tZXRyaWNzXG4gICAgICAgIGV2ZW50cyAtIHNlbmQgZGF0YSBmcm9tIHRhYmxlIHN5c3RlbS5ldmVudHNcbiAgICAgICAgYXN5bmNocm9ub3VzX21ldHJpY3MgLSBzZW5kIGRhdGEgZnJvbSB0YWJsZSBzeXN0ZW0uYXN5bmNocm9ub3VzX21ldHJpY3NcbiAgICAgICAgc3RhdHVzX2luZm8gLSBzZW5kIGRhdGEgZnJvbSBkaWZmZXJlbnQgY29tcG9uZW50IGZyb20gQ0gsIGV4OiBEaWN0aW9uYXJpZXMgc3RhdHVzXG4gICAgLS0+XG4gICAgPCEtLVxuICAgIDxwcm9tZXRoZXVzPlxuICAgICAgICA8ZW5kcG9pbnQ+L21ldHJpY3M8L2VuZHBvaW50PlxuICAgICAgICA8cG9ydD45MzYzPC9wb3J0PlxuXG4gICAgICAgIDxtZXRyaWNzPnRydWU8L21ldHJpY3M+XG4gICAgICAgIDxldmVudHM+dHJ1ZTwvZXZlbnRzPlxuICAgICAgICA8YXN5bmNocm9ub3VzX21ldHJpY3M+dHJ1ZTwvYXN5bmNocm9ub3VzX21ldHJpY3M+XG4gICAgICAgIDxzdGF0dXNfaW5mbz50cnVlPC9zdGF0dXNfaW5mbz5cbiAgICA8L3Byb21ldGhldXM+XG4gICAgLS0+XG5cbiAgICA8IS0tIFF1ZXJ5IGxvZy4gVXNlZCBvbmx5IGZvciBxdWVyaWVzIHdpdGggc2V0dGluZyBsb2dfcXVlcmllcyA9IDEuIC0tPlxuICAgIDxxdWVyeV9sb2c+XG4gICAgICAgIDwhLS0gV2hhdCB0YWJsZSB0byBpbnNlcnQgZGF0YS4gSWYgdGFibGUgaXMgbm90IGV4aXN0LCBpdCB3aWxsIGJlIGNyZWF0ZWQuXG4gICAgICAgICAgICBXaGVuIHF1ZXJ5IGxvZyBzdHJ1Y3R1cmUgaXMgY2hhbmdlZCBhZnRlciBzeXN0ZW0gdXBkYXRlLFxuICAgICAgICAgICAgICB0aGVuIG9sZCB0YWJsZSB3aWxsIGJlIHJlbmFtZWQgYW5kIG5ldyB0YWJsZSB3aWxsIGJlIGNyZWF0ZWQgYXV0b21hdGljYWxseS5cbiAgICAgICAgLS0+XG4gICAgICAgIDxkYXRhYmFzZT5zeXN0ZW08L2RhdGFiYXNlPlxuICAgICAgICA8dGFibGU+cXVlcnlfbG9nPC90YWJsZT5cbiAgICAgICAgPCEtLVxuICAgICAgICAgICAgUEFSVElUSU9OIEJZIGV4cHI6XG4gICAgICAgIGh0dHBzOi8vY2xpY2tob3VzZS5jb20vZG9jcy9lbi90YWJsZV9lbmdpbmVzL21lcmdldHJlZS1mYW1pbHkvY3VzdG9tX3BhcnRpdGlvbmluZ19rZXkvXG4gICAgICAgICAgICBFeGFtcGxlOlxuICAgICAgICAgICAgICAgIGV2ZW50X2RhdGVcbiAgICAgICAgICAgICAgICB0b01vbmRheShldmVudF9kYXRlKVxuICAgICAgICAgICAgICAgIHRvWVlZWU1NKGV2ZW50X2RhdGUpXG4gICAgICAgICAgICAgICAgdG9TdGFydE9mSG91cihldmVudF90aW1lKVxuICAgICAgICAtLT5cbiAgICAgICAgPHBhcnRpdGlvbl9ieT50b1lZWVlNTShldmVudF9kYXRlKTwvcGFydGl0aW9uX2J5PlxuICAgICAgICA8IS0tXG4gICAgICAgICAgICBUYWJsZSBUVEwgc3BlY2lmaWNhdGlvbjpcbiAgICAgICAgaHR0cHM6Ly9jbGlja2hvdXNlLmNvbS9kb2NzL2VuL2VuZ2luZXMvdGFibGUtZW5naW5lcy9tZXJnZXRyZWUtZmFtaWx5L21lcmdldHJlZS8jbWVyZ2V0cmVlLXRhYmxlLXR0bFxuICAgICAgICAgICAgRXhhbXBsZTpcbiAgICAgICAgICAgICAgICBldmVudF9kYXRlICsgSU5URVJWQUwgMSBXRUVLXG4gICAgICAgICAgICAgICAgZXZlbnRfZGF0ZSArIElOVEVSVkFMIDcgREFZIERFTEVURVxuICAgICAgICAgICAgICAgIGV2ZW50X2RhdGUgKyBJTlRFUlZBTCAyIFdFRUsgVE8gRElTSyAnYmJiJ1xuXG4gICAgICAgIDx0dGw+ZXZlbnRfZGF0ZSArIElOVEVSVkFMIDMwIERBWSBERUxFVEU8L3R0bD5cbiAgICAgICAgLS0+XG5cbiAgICAgICAgPCEtLSBJbnN0ZWFkIG9mIHBhcnRpdGlvbl9ieSwgeW91IGNhbiBwcm92aWRlIGZ1bGwgZW5naW5lIGV4cHJlc3Npb24gKHN0YXJ0aW5nIHdpdGggRU5HSU5FID1cbiAgICAgICAgKSB3aXRoIHBhcmFtZXRlcnMsXG4gICAgICAgICAgICBFeGFtcGxlOiA8ZW5naW5lPkVOR0lORSA9IE1lcmdlVHJlZSBQQVJUSVRJT04gQlkgdG9ZWVlZTU0oZXZlbnRfZGF0ZSkgT1JERVIgQlkgKGV2ZW50X2RhdGUsXG4gICAgICAgIGV2ZW50X3RpbWUpIFNFVFRJTkdTIGluZGV4X2dyYW51bGFyaXR5ID0gMTAyNDwvZW5naW5lPlxuICAgICAgICAgIC0tPlxuXG4gICAgICAgIDwhLS0gSW50ZXJ2YWwgb2YgZmx1c2hpbmcgZGF0YS4gLS0+XG4gICAgICAgIDxmbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+NzUwMDwvZmx1c2hfaW50ZXJ2YWxfbWlsbGlzZWNvbmRzPlxuICAgIDwvcXVlcnlfbG9nPlxuXG4gICAgPCEtLSBUcmFjZSBsb2cuIFN0b3JlcyBzdGFjayB0cmFjZXMgY29sbGVjdGVkIGJ5IHF1ZXJ5IHByb2ZpbGVycy5cbiAgICAgICAgU2VlIHF1ZXJ5X3Byb2ZpbGVyX3JlYWxfdGltZV9wZXJpb2RfbnMgYW5kIHF1ZXJ5X3Byb2ZpbGVyX2NwdV90aW1lX3BlcmlvZF9ucyBzZXR0aW5ncy4gLS0+XG4gICAgPHRyYWNlX2xvZz5cbiAgICAgICAgPGRhdGFiYXNlPnN5c3RlbTwvZGF0YWJhc2U+XG4gICAgICAgIDx0YWJsZT50cmFjZV9sb2c8L3RhYmxlPlxuXG4gICAgICAgIDxwYXJ0aXRpb25fYnk+dG9ZWVlZTU0oZXZlbnRfZGF0ZSk8L3BhcnRpdGlvbl9ieT5cbiAgICAgICAgPGZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz43NTAwPC9mbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+XG4gICAgPC90cmFjZV9sb2c+XG5cbiAgICA8IS0tIFF1ZXJ5IHRocmVhZCBsb2cuIEhhcyBpbmZvcm1hdGlvbiBhYm91dCBhbGwgdGhyZWFkcyBwYXJ0aWNpcGF0ZWQgaW4gcXVlcnkgZXhlY3V0aW9uLlxuICAgICAgICBVc2VkIG9ubHkgZm9yIHF1ZXJpZXMgd2l0aCBzZXR0aW5nIGxvZ19xdWVyeV90aHJlYWRzID0gMS4gLS0+XG4gICAgPHF1ZXJ5X3RocmVhZF9sb2c+XG4gICAgICAgIDxkYXRhYmFzZT5zeXN0ZW08L2RhdGFiYXNlPlxuICAgICAgICA8dGFibGU+cXVlcnlfdGhyZWFkX2xvZzwvdGFibGU+XG4gICAgICAgIDxwYXJ0aXRpb25fYnk+dG9ZWVlZTU0oZXZlbnRfZGF0ZSk8L3BhcnRpdGlvbl9ieT5cbiAgICAgICAgPGZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz43NTAwPC9mbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+XG4gICAgPC9xdWVyeV90aHJlYWRfbG9nPlxuXG4gICAgPCEtLSBRdWVyeSB2aWV3cyBsb2cuIEhhcyBpbmZvcm1hdGlvbiBhYm91dCBhbGwgZGVwZW5kZW50IHZpZXdzIGFzc29jaWF0ZWQgd2l0aCBhIHF1ZXJ5LlxuICAgICAgICBVc2VkIG9ubHkgZm9yIHF1ZXJpZXMgd2l0aCBzZXR0aW5nIGxvZ19xdWVyeV92aWV3cyA9IDEuIC0tPlxuICAgIDxxdWVyeV92aWV3c19sb2c+XG4gICAgICAgIDxkYXRhYmFzZT5zeXN0ZW08L2RhdGFiYXNlPlxuICAgICAgICA8dGFibGU+cXVlcnlfdmlld3NfbG9nPC90YWJsZT5cbiAgICAgICAgPHBhcnRpdGlvbl9ieT50b1lZWVlNTShldmVudF9kYXRlKTwvcGFydGl0aW9uX2J5PlxuICAgICAgICA8Zmx1c2hfaW50ZXJ2YWxfbWlsbGlzZWNvbmRzPjc1MDA8L2ZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz5cbiAgICA8L3F1ZXJ5X3ZpZXdzX2xvZz5cblxuICAgIDwhLS0gVW5jb21tZW50IGlmIHVzZSBwYXJ0IGxvZy5cbiAgICAgICAgUGFydCBsb2cgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgYWxsIGFjdGlvbnMgd2l0aCBwYXJ0cyBpbiBNZXJnZVRyZWUgdGFibGVzIChjcmVhdGlvbiwgZGVsZXRpb24sXG4gICAgbWVyZ2VzLCBkb3dubG9hZHMpLi0tPlxuICAgIDxwYXJ0X2xvZz5cbiAgICAgICAgPGRhdGFiYXNlPnN5c3RlbTwvZGF0YWJhc2U+XG4gICAgICAgIDx0YWJsZT5wYXJ0X2xvZzwvdGFibGU+XG4gICAgICAgIDxwYXJ0aXRpb25fYnk+dG9ZWVlZTU0oZXZlbnRfZGF0ZSk8L3BhcnRpdGlvbl9ieT5cbiAgICAgICAgPGZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz43NTAwPC9mbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+XG4gICAgPC9wYXJ0X2xvZz5cblxuICAgIDwhLS0gVW5jb21tZW50IHRvIHdyaXRlIHRleHQgbG9nIGludG8gdGFibGUuXG4gICAgICAgIFRleHQgbG9nIGNvbnRhaW5zIGFsbCBpbmZvcm1hdGlvbiBmcm9tIHVzdWFsIHNlcnZlciBsb2cgYnV0IHN0b3JlcyBpdCBpbiBzdHJ1Y3R1cmVkIGFuZCBlZmZpY2llbnRcbiAgICB3YXkuXG4gICAgICAgIFRoZSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCBnb2VzIHRvIHRoZSB0YWJsZSBjYW4gYmUgbGltaXRlZCAoPGxldmVsPiksIGlmIG5vdCBzcGVjaWZpZWQgYWxsXG4gICAgbWVzc2FnZXMgd2lsbCBnbyB0byB0aGUgdGFibGUuXG4gICAgPHRleHRfbG9nPlxuICAgICAgICA8ZGF0YWJhc2U+c3lzdGVtPC9kYXRhYmFzZT5cbiAgICAgICAgPHRhYmxlPnRleHRfbG9nPC90YWJsZT5cbiAgICAgICAgPGZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz43NTAwPC9mbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+XG4gICAgICAgIDxsZXZlbD48L2xldmVsPlxuICAgIDwvdGV4dF9sb2c+XG4gICAgLS0+XG5cbiAgICA8IS0tIE1ldHJpYyBsb2cgY29udGFpbnMgcm93cyB3aXRoIGN1cnJlbnQgdmFsdWVzIG9mIFByb2ZpbGVFdmVudHMsIEN1cnJlbnRNZXRyaWNzIGNvbGxlY3RlZFxuICAgIHdpdGggXCJjb2xsZWN0X2ludGVydmFsX21pbGxpc2Vjb25kc1wiIGludGVydmFsLiAtLT5cbiAgICA8bWV0cmljX2xvZz5cbiAgICAgICAgPGRhdGFiYXNlPnN5c3RlbTwvZGF0YWJhc2U+XG4gICAgICAgIDx0YWJsZT5tZXRyaWNfbG9nPC90YWJsZT5cbiAgICAgICAgPGZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz43NTAwPC9mbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+XG4gICAgICAgIDxjb2xsZWN0X2ludGVydmFsX21pbGxpc2Vjb25kcz4xMDAwPC9jb2xsZWN0X2ludGVydmFsX21pbGxpc2Vjb25kcz5cbiAgICA8L21ldHJpY19sb2c+XG5cbiAgICA8IS0tXG4gICAgICAgIEFzeW5jaHJvbm91cyBtZXRyaWMgbG9nIGNvbnRhaW5zIHZhbHVlcyBvZiBtZXRyaWNzIGZyb21cbiAgICAgICAgc3lzdGVtLmFzeW5jaHJvbm91c19tZXRyaWNzLlxuICAgIC0tPlxuICAgIDxhc3luY2hyb25vdXNfbWV0cmljX2xvZz5cbiAgICAgICAgPGRhdGFiYXNlPnN5c3RlbTwvZGF0YWJhc2U+XG4gICAgICAgIDx0YWJsZT5hc3luY2hyb25vdXNfbWV0cmljX2xvZzwvdGFibGU+XG4gICAgICAgIDwhLS1cbiAgICAgICAgICAgIEFzeW5jaHJvbm91cyBtZXRyaWNzIGFyZSB1cGRhdGVkIG9uY2UgYSBtaW51dGUsIHNvIHRoZXJlIGlzXG4gICAgICAgICAgICBubyBuZWVkIHRvIGZsdXNoIG1vcmUgb2Z0ZW4uXG4gICAgICAgIC0tPlxuICAgICAgICA8Zmx1c2hfaW50ZXJ2YWxfbWlsbGlzZWNvbmRzPjcwMDA8L2ZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz5cbiAgICA8L2FzeW5jaHJvbm91c19tZXRyaWNfbG9nPlxuXG4gICAgPCEtLVxuICAgICAgICBPcGVuVGVsZW1ldHJ5IGxvZyBjb250YWlucyBPcGVuVGVsZW1ldHJ5IHRyYWNlIHNwYW5zLlxuICAgIC0tPlxuICAgIDxvcGVudGVsZW1ldHJ5X3NwYW5fbG9nPlxuICAgICAgICA8IS0tXG4gICAgICAgICAgICBUaGUgZGVmYXVsdCB0YWJsZSBjcmVhdGlvbiBjb2RlIGlzIGluc3VmZmljaWVudCwgdGhpcyA8ZW5naW5lPiBzcGVjXG4gICAgICAgICAgICBpcyBhIHdvcmthcm91bmQuIFRoZXJlIGlzIG5vICdldmVudF90aW1lJyBmb3IgdGhpcyBsb2csIGJ1dCB0d28gdGltZXMsXG4gICAgICAgICAgICBzdGFydCBhbmQgZmluaXNoLiBJdCBpcyBzb3J0ZWQgYnkgZmluaXNoIHRpbWUsIHRvIGF2b2lkIGluc2VydGluZ1xuICAgICAgICAgICAgZGF0YSB0b28gZmFyIGF3YXkgaW4gdGhlIHBhc3QgKHByb2JhYmx5IHdlIGNhbiBzb21ldGltZXMgaW5zZXJ0IGEgc3BhblxuICAgICAgICAgICAgdGhhdCBpcyBzZWNvbmRzIGVhcmxpZXIgdGhhbiB0aGUgbGFzdCBzcGFuIGluIHRoZSB0YWJsZSwgZHVlIHRvIGEgcmFjZVxuICAgICAgICAgICAgYmV0d2VlbiBzZXZlcmFsIHNwYW5zIGluc2VydGVkIGluIHBhcmFsbGVsKS4gVGhpcyBnaXZlcyB0aGUgc3BhbnMgYVxuICAgICAgICAgICAgZ2xvYmFsIG9yZGVyIHRoYXQgd2UgY2FuIHVzZSB0byBlLmcuIHJldHJ5IGluc2VydGlvbiBpbnRvIHNvbWUgZXh0ZXJuYWxcbiAgICAgICAgICAgIHN5c3RlbS5cbiAgICAgICAgLS0+XG4gICAgICAgIDxlbmdpbmU+XG4gICAgICAgICAgICBlbmdpbmUgTWVyZ2VUcmVlXG4gICAgICAgICAgICBwYXJ0aXRpb24gYnkgdG9ZWVlZTU0oZmluaXNoX2RhdGUpXG4gICAgICAgICAgICBvcmRlciBieSAoZmluaXNoX2RhdGUsIGZpbmlzaF90aW1lX3VzLCB0cmFjZV9pZClcbiAgICAgICAgPC9lbmdpbmU+XG4gICAgICAgIDxkYXRhYmFzZT5zeXN0ZW08L2RhdGFiYXNlPlxuICAgICAgICA8dGFibGU+b3BlbnRlbGVtZXRyeV9zcGFuX2xvZzwvdGFibGU+XG4gICAgICAgIDxmbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+NzUwMDwvZmx1c2hfaW50ZXJ2YWxfbWlsbGlzZWNvbmRzPlxuICAgIDwvb3BlbnRlbGVtZXRyeV9zcGFuX2xvZz5cblxuXG4gICAgPCEtLSBDcmFzaCBsb2cuIFN0b3JlcyBzdGFjayB0cmFjZXMgZm9yIGZhdGFsIGVycm9ycy5cbiAgICAgICAgVGhpcyB0YWJsZSBpcyBub3JtYWxseSBlbXB0eS4gLS0+XG4gICAgPGNyYXNoX2xvZz5cbiAgICAgICAgPGRhdGFiYXNlPnN5c3RlbTwvZGF0YWJhc2U+XG4gICAgICAgIDx0YWJsZT5jcmFzaF9sb2c8L3RhYmxlPlxuXG4gICAgICAgIDxwYXJ0aXRpb25fYnkgLz5cbiAgICAgICAgPGZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz4xMDAwPC9mbHVzaF9pbnRlcnZhbF9taWxsaXNlY29uZHM+XG4gICAgPC9jcmFzaF9sb2c+XG5cbiAgICA8IS0tIFNlc3Npb24gbG9nLiBTdG9yZXMgdXNlciBsb2cgaW4gKHN1Y2Nlc3NmdWwgb3Igbm90KSBhbmQgbG9nIG91dCBldmVudHMuIC0tPlxuICAgIDxzZXNzaW9uX2xvZz5cbiAgICAgICAgPGRhdGFiYXNlPnN5c3RlbTwvZGF0YWJhc2U+XG4gICAgICAgIDx0YWJsZT5zZXNzaW9uX2xvZzwvdGFibGU+XG5cbiAgICAgICAgPHBhcnRpdGlvbl9ieT50b1lZWVlNTShldmVudF9kYXRlKTwvcGFydGl0aW9uX2J5PlxuICAgICAgICA8Zmx1c2hfaW50ZXJ2YWxfbWlsbGlzZWNvbmRzPjc1MDA8L2ZsdXNoX2ludGVydmFsX21pbGxpc2Vjb25kcz5cbiAgICA8L3Nlc3Npb25fbG9nPlxuXG4gICAgPCEtLSBQYXJhbWV0ZXJzIGZvciBlbWJlZGRlZCBkaWN0aW9uYXJpZXMsIHVzZWQgaW4gWWFuZGV4Lk1ldHJpY2EuXG4gICAgICAgIFNlZSBodHRwczovL2NsaWNraG91c2UuY29tL2RvY3MvZW4vZGljdHMvaW50ZXJuYWxfZGljdHMvXG4gICAgLS0+XG5cbiAgICA8IS0tIFBhdGggdG8gZmlsZSB3aXRoIHJlZ2lvbiBoaWVyYXJjaHkuIC0tPlxuICAgIDwhLS1cbiAgICA8cGF0aF90b19yZWdpb25zX2hpZXJhcmNoeV9maWxlPi9vcHQvZ2VvL3JlZ2lvbnNfaGllcmFyY2h5LnR4dDwvcGF0aF90b19yZWdpb25zX2hpZXJhcmNoeV9maWxlPiAtLT5cblxuICAgIDwhLS0gUGF0aCB0byBkaXJlY3Rvcnkgd2l0aCBmaWxlcyBjb250YWluaW5nIG5hbWVzIG9mIHJlZ2lvbnMgLS0+XG4gICAgPCEtLSA8cGF0aF90b19yZWdpb25zX25hbWVzX2ZpbGVzPi9vcHQvZ2VvLzwvcGF0aF90b19yZWdpb25zX25hbWVzX2ZpbGVzPiAtLT5cblxuXG4gICAgPCEtLSA8dG9wX2xldmVsX2RvbWFpbnNfcGF0aD4vdmFyL2xpYi9jbGlja2hvdXNlL3RvcF9sZXZlbF9kb21haW5zLzwvdG9wX2xldmVsX2RvbWFpbnNfcGF0aD4gLS0+XG4gICAgPCEtLSBDdXN0b20gVExEIGxpc3RzLlxuICAgICAgICBGb3JtYXQ6IDxuYW1lPi9wYXRoL3RvL2ZpbGU8L25hbWU+XG5cbiAgICAgICAgQ2hhbmdlcyB3aWxsIG5vdCBiZSBhcHBsaWVkIHcvbyBzZXJ2ZXIgcmVzdGFydC5cbiAgICAgICAgUGF0aCB0byB0aGUgbGlzdCBpcyB1bmRlciB0b3BfbGV2ZWxfZG9tYWluc19wYXRoIChzZWUgYWJvdmUpLlxuICAgIC0tPlxuICAgIDx0b3BfbGV2ZWxfZG9tYWluc19saXN0cz5cbiAgICAgICAgPCEtLVxuICAgICAgICA8cHVibGljX3N1ZmZpeF9saXN0Pi9wYXRoL3RvL3B1YmxpY19zdWZmaXhfbGlzdC5kYXQ8L3B1YmxpY19zdWZmaXhfbGlzdD5cbiAgICAgICAgLS0+XG4gICAgPC90b3BfbGV2ZWxfZG9tYWluc19saXN0cz5cblxuICAgIDwhLS0gQ29uZmlndXJhdGlvbiBvZiBleHRlcm5hbCBkaWN0aW9uYXJpZXMuIFNlZTpcbiAgICAgICAgaHR0cHM6Ly9jbGlja2hvdXNlLmNvbS9kb2NzL2VuL3NxbC1yZWZlcmVuY2UvZGljdGlvbmFyaWVzL2V4dGVybmFsLWRpY3Rpb25hcmllcy9leHRlcm5hbC1kaWN0c1xuICAgIC0tPlxuICAgIDxkaWN0aW9uYXJpZXNfY29uZmlnPipfZGljdGlvbmFyeS54bWw8L2RpY3Rpb25hcmllc19jb25maWc+XG5cbiAgICA8IS0tIENvbmZpZ3VyYXRpb24gb2YgdXNlciBkZWZpbmVkIGV4ZWN1dGFibGUgZnVuY3Rpb25zIC0tPlxuICAgIDx1c2VyX2RlZmluZWRfZXhlY3V0YWJsZV9mdW5jdGlvbnNfY29uZmlnPipfZnVuY3Rpb24ueG1sPC91c2VyX2RlZmluZWRfZXhlY3V0YWJsZV9mdW5jdGlvbnNfY29uZmlnPlxuXG4gICAgPCEtLSBVbmNvbW1lbnQgaWYgeW91IHdhbnQgZGF0YSB0byBiZSBjb21wcmVzc2VkIDMwLTEwMCUgYmV0dGVyLlxuICAgICAgICBEb24ndCBkbyB0aGF0IGlmIHlvdSBqdXN0IHN0YXJ0ZWQgdXNpbmcgQ2xpY2tIb3VzZS5cbiAgICAgIC0tPlxuICAgIDwhLS1cbiAgICA8Y29tcHJlc3Npb24+XG4gICAgICAgIDwhLSAtIFNldCBvZiB2YXJpYW50cy4gQ2hlY2tlZCBpbiBvcmRlci4gTGFzdCBtYXRjaGluZyBjYXNlIHdpbnMuIElmIG5vdGhpbmcgbWF0Y2hlcywgbHo0IHdpbGwgYmVcbiAgICB1c2VkLiAtIC0+XG4gICAgICAgIDxjYXNlPlxuXG4gICAgICAgICAgICA8IS0gLSBDb25kaXRpb25zLiBBbGwgbXVzdCBiZSBzYXRpc2ZpZWQuIFNvbWUgY29uZGl0aW9ucyBtYXkgYmUgb21pdHRlZC4gLSAtPlxuICAgICAgICAgICAgPG1pbl9wYXJ0X3NpemU+MTAwMDAwMDAwMDA8L21pbl9wYXJ0X3NpemU+ICAgICAgICA8IS0gLSBNaW4gcGFydCBzaXplIGluIGJ5dGVzLiAtIC0+XG4gICAgICAgICAgICA8bWluX3BhcnRfc2l6ZV9yYXRpbz4wLjAxPC9taW5fcGFydF9zaXplX3JhdGlvPiAgIDwhLSAtIE1pbiBzaXplIG9mIHBhcnQgcmVsYXRpdmUgdG8gd2hvbGUgdGFibGVcbiAgICBzaXplLiAtIC0+XG5cbiAgICAgICAgICAgIDwhLSAtIFdoYXQgY29tcHJlc3Npb24gbWV0aG9kIHRvIHVzZS4gLSAtPlxuICAgICAgICAgICAgPG1ldGhvZD56c3RkPC9tZXRob2Q+XG4gICAgICAgIDwvY2FzZT5cbiAgICA8L2NvbXByZXNzaW9uPlxuICAgIC0tPlxuXG4gICAgPCEtLSBDb25maWd1cmF0aW9uIG9mIGVuY3J5cHRpb24uIFRoZSBzZXJ2ZXIgZXhlY3V0ZXMgYSBjb21tYW5kIHRvXG4gICAgICAgIG9idGFpbiBhbiBlbmNyeXB0aW9uIGtleSBhdCBzdGFydHVwIGlmIHN1Y2ggYSBjb21tYW5kIGlzXG4gICAgICAgIGRlZmluZWQsIG9yIGVuY3J5cHRpb24gY29kZWNzIHdpbGwgYmUgZGlzYWJsZWQgb3RoZXJ3aXNlLiBUaGVcbiAgICAgICAgY29tbWFuZCBpcyBleGVjdXRlZCB0aHJvdWdoIC9iaW4vc2ggYW5kIGlzIGV4cGVjdGVkIHRvIHdyaXRlXG4gICAgICAgIGEgQmFzZTY0LWVuY29kZWQga2V5IHRvIHRoZSBzdGRvdXQuIC0tPlxuICAgIDxlbmNyeXB0aW9uX2NvZGVjcz5cbiAgICAgICAgPCEtLSBhZXNfMTI4X2djbV9zaXYgLS0+XG4gICAgICAgIDwhLS0gRXhhbXBsZSBvZiBnZXR0aW5nIGhleCBrZXkgZnJvbSBlbnYgLS0+XG4gICAgICAgIDwhLS0gdGhlIGNvZGUgc2hvdWxkIHVzZSB0aGlzIGtleSBhbmQgdGhyb3cgYW4gZXhjZXB0aW9uIGlmIGl0cyBsZW5ndGggaXMgbm90IDE2IGJ5dGVzIC0tPlxuICAgICAgICA8IS0ta2V5X2hleFxuICAgICAgICBmcm9tX2Vudj1cIi4uLlwiPjwva2V5X2hleCAtLT5cblxuICAgICAgICA8IS0tIEV4YW1wbGUgb2YgbXVsdGlwbGUgaGV4IGtleXMuIFRoZXkgY2FuIGJlIGltcG9ydGVkIGZyb20gZW52IG9yIGJlIHdyaXR0ZW4gZG93biBpblxuICAgICAgICBjb25maWctLT5cbiAgICAgICAgPCEtLSB0aGUgY29kZSBzaG91bGQgdXNlIHRoZXNlIGtleXMgYW5kIHRocm93IGFuIGV4Y2VwdGlvbiBpZiB0aGVpciBsZW5ndGggaXMgbm90IDE2IGJ5dGVzIC0tPlxuICAgICAgICA8IS0tIGtleV9oZXggaWQ9XCIwXCI+Li4uPC9rZXlfaGV4IC0tPlxuICAgICAgICA8IS0tIGtleV9oZXggaWQ9XCIxXCIgZnJvbV9lbnY9XCIuLlwiPjwva2V5X2hleCAtLT5cbiAgICAgICAgPCEtLSBrZXlfaGV4IGlkPVwiMlwiPi4uLjwva2V5X2hleCAtLT5cbiAgICAgICAgPCEtLSBjdXJyZW50X2tleV9pZD4yPC9jdXJyZW50X2tleV9pZCAtLT5cblxuICAgICAgICA8IS0tIEV4YW1wbGUgb2YgZ2V0dGluZyBoZXgga2V5IGZyb20gY29uZmlnIC0tPlxuICAgICAgICA8IS0tIHRoZSBjb2RlIHNob3VsZCB1c2UgdGhpcyBrZXkgYW5kIHRocm93IGFuIGV4Y2VwdGlvbiBpZiBpdHMgbGVuZ3RoIGlzIG5vdCAxNiBieXRlcyAtLT5cbiAgICAgICAgPCEtLSBrZXk+Li4uPC9rZXkgLS0+XG5cbiAgICAgICAgPCEtLSBleGFtcGxlIG9mIGFkZGluZyBub25jZSAtLT5cbiAgICAgICAgPCEtLSBub25jZT4uLi48L25vbmNlIC0tPlxuXG4gICAgICAgIDwhLS0gL2Flc18xMjhfZ2NtX3NpdiAtLT5cbiAgICA8L2VuY3J5cHRpb25fY29kZWNzPlxuXG4gICAgPCEtLSBBbGxvdyB0byBleGVjdXRlIGRpc3RyaWJ1dGVkIERETCBxdWVyaWVzIChDUkVBVEUsIERST1AsIEFMVEVSLCBSRU5BTUUpIG9uIGNsdXN0ZXIuXG4gICAgICAgIFdvcmtzIG9ubHkgaWYgWm9vS2VlcGVyIGlzIGVuYWJsZWQuIENvbW1lbnQgaXQgaWYgc3VjaCBmdW5jdGlvbmFsaXR5IGlzbid0IHJlcXVpcmVkLiAtLT5cbiAgICA8ZGlzdHJpYnV0ZWRfZGRsPlxuICAgICAgICA8IS0tIFBhdGggaW4gWm9vS2VlcGVyIHRvIHF1ZXVlIHdpdGggRERMIHF1ZXJpZXMgLS0+XG4gICAgICAgIDxwYXRoPi9jbGlja2hvdXNlL3Rhc2tfcXVldWUvZGRsPC9wYXRoPlxuXG4gICAgICAgIDwhLS0gU2V0dGluZ3MgZnJvbSB0aGlzIHByb2ZpbGUgd2lsbCBiZSB1c2VkIHRvIGV4ZWN1dGUgRERMIHF1ZXJpZXMgLS0+XG4gICAgICAgIDwhLS0gPHByb2ZpbGU+ZGVmYXVsdDwvcHJvZmlsZT4gLS0+XG5cbiAgICAgICAgPCEtLSBDb250cm9scyBob3cgbXVjaCBPTiBDTFVTVEVSIHF1ZXJpZXMgY2FuIGJlIHJ1biBzaW11bHRhbmVvdXNseS4gLS0+XG4gICAgICAgIDwhLS0gPHBvb2xfc2l6ZT4xPC9wb29sX3NpemU+IC0tPlxuXG4gICAgICAgIDwhLS1cbiAgICAgICAgICAgIENsZWFudXAgc2V0dGluZ3MgKGFjdGl2ZSB0YXNrcyB3aWxsIG5vdCBiZSByZW1vdmVkKVxuICAgICAgICAtLT5cblxuICAgICAgICA8IS0tIENvbnRyb2xzIHRhc2sgVFRMIChkZWZhdWx0IDEgd2VlaykgLS0+XG4gICAgICAgIDwhLS0gPHRhc2tfbWF4X2xpZmV0aW1lPjYwNDgwMDwvdGFza19tYXhfbGlmZXRpbWU+IC0tPlxuXG4gICAgICAgIDwhLS0gQ29udHJvbHMgaG93IG9mdGVuIGNsZWFudXAgc2hvdWxkIGJlIHBlcmZvcm1lZCAoaW4gc2Vjb25kcykgLS0+XG4gICAgICAgIDwhLS0gPGNsZWFudXBfZGVsYXlfcGVyaW9kPjYwPC9jbGVhbnVwX2RlbGF5X3BlcmlvZD4gLS0+XG5cbiAgICAgICAgPCEtLSBDb250cm9scyBob3cgbWFueSB0YXNrcyBjb3VsZCBiZSBpbiB0aGUgcXVldWUgLS0+XG4gICAgICAgIDwhLS0gPG1heF90YXNrc19pbl9xdWV1ZT4xMDAwPC9tYXhfdGFza3NfaW5fcXVldWU+IC0tPlxuICAgIDwvZGlzdHJpYnV0ZWRfZGRsPlxuXG4gICAgPCEtLSBTZXR0aW5ncyB0byBmaW5lIHR1bmUgTWVyZ2VUcmVlIHRhYmxlcy4gU2VlIGRvY3VtZW50YXRpb24gaW4gc291cmNlIGNvZGUsIGluXG4gICAgTWVyZ2VUcmVlU2V0dGluZ3MuaCAtLT5cbiAgICA8IS0tXG4gICAgPG1lcmdlX3RyZWU+XG4gICAgICAgIDxtYXhfc3VzcGljaW91c19icm9rZW5fcGFydHM+NTwvbWF4X3N1c3BpY2lvdXNfYnJva2VuX3BhcnRzPlxuICAgIDwvbWVyZ2VfdHJlZT5cbiAgICAtLT5cblxuICAgIDwhLS0gUHJvdGVjdGlvbiBmcm9tIGFjY2lkZW50YWwgRFJPUC5cbiAgICAgICAgSWYgc2l6ZSBvZiBhIE1lcmdlVHJlZSB0YWJsZSBpcyBncmVhdGVyIHRoYW4gbWF4X3RhYmxlX3NpemVfdG9fZHJvcCAoaW4gYnl0ZXMpIHRoYW4gdGFibGUgY291bGQgbm90XG4gICAgYmUgZHJvcHBlZCB3aXRoIGFueSBEUk9QIHF1ZXJ5LlxuICAgICAgICBJZiB5b3Ugd2FudCBkbyBkZWxldGUgb25lIHRhYmxlIGFuZCBkb24ndCB3YW50IHRvIGNoYW5nZSBjbGlja2hvdXNlLXNlcnZlciBjb25maWcsIHlvdSBjb3VsZCBjcmVhdGVcbiAgICBzcGVjaWFsIGZpbGUgPGNsaWNraG91c2UtcGF0aD4vZmxhZ3MvZm9yY2VfZHJvcF90YWJsZSBhbmQgbWFrZSBEUk9QIG9uY2UuXG4gICAgICAgIEJ5IGRlZmF1bHQgbWF4X3RhYmxlX3NpemVfdG9fZHJvcCBpcyA1MEdCOyBtYXhfdGFibGVfc2l6ZV90b19kcm9wPTAgYWxsb3dzIHRvIERST1AgYW55IHRhYmxlcy5cbiAgICAgICAgVGhlIHNhbWUgZm9yIG1heF9wYXJ0aXRpb25fc2l6ZV90b19kcm9wLlxuICAgICAgICBVbmNvbW1lbnQgdG8gZGlzYWJsZSBwcm90ZWN0aW9uLlxuICAgIC0tPlxuICAgIDwhLS0gPG1heF90YWJsZV9zaXplX3RvX2Ryb3A+MDwvbWF4X3RhYmxlX3NpemVfdG9fZHJvcD4gLS0+XG4gICAgPCEtLSA8bWF4X3BhcnRpdGlvbl9zaXplX3RvX2Ryb3A+MDwvbWF4X3BhcnRpdGlvbl9zaXplX3RvX2Ryb3A+IC0tPlxuXG4gICAgPCEtLSBFeGFtcGxlIG9mIHBhcmFtZXRlcnMgZm9yIEdyYXBoaXRlTWVyZ2VUcmVlIHRhYmxlIGVuZ2luZSAtLT5cbiAgICA8Z3JhcGhpdGVfcm9sbHVwX2V4YW1wbGU+XG4gICAgICAgIDxwYXR0ZXJuPlxuICAgICAgICAgICAgPHJlZ2V4cD5jbGlja19jb3N0PC9yZWdleHA+XG4gICAgICAgICAgICA8ZnVuY3Rpb24+YW55PC9mdW5jdGlvbj5cbiAgICAgICAgICAgIDxyZXRlbnRpb24+XG4gICAgICAgICAgICAgICAgPGFnZT4wPC9hZ2U+XG4gICAgICAgICAgICAgICAgPHByZWNpc2lvbj4zNjAwPC9wcmVjaXNpb24+XG4gICAgICAgICAgICA8L3JldGVudGlvbj5cbiAgICAgICAgICAgIDxyZXRlbnRpb24+XG4gICAgICAgICAgICAgICAgPGFnZT44NjQwMDwvYWdlPlxuICAgICAgICAgICAgICAgIDxwcmVjaXNpb24+NjA8L3ByZWNpc2lvbj5cbiAgICAgICAgICAgIDwvcmV0ZW50aW9uPlxuICAgICAgICA8L3BhdHRlcm4+XG4gICAgICAgIDxkZWZhdWx0PlxuICAgICAgICAgICAgPGZ1bmN0aW9uPm1heDwvZnVuY3Rpb24+XG4gICAgICAgICAgICA8cmV0ZW50aW9uPlxuICAgICAgICAgICAgICAgIDxhZ2U+MDwvYWdlPlxuICAgICAgICAgICAgICAgIDxwcmVjaXNpb24+NjA8L3ByZWNpc2lvbj5cbiAgICAgICAgICAgIDwvcmV0ZW50aW9uPlxuICAgICAgICAgICAgPHJldGVudGlvbj5cbiAgICAgICAgICAgICAgICA8YWdlPjM2MDA8L2FnZT5cbiAgICAgICAgICAgICAgICA8cHJlY2lzaW9uPjMwMDwvcHJlY2lzaW9uPlxuICAgICAgICAgICAgPC9yZXRlbnRpb24+XG4gICAgICAgICAgICA8cmV0ZW50aW9uPlxuICAgICAgICAgICAgICAgIDxhZ2U+ODY0MDA8L2FnZT5cbiAgICAgICAgICAgICAgICA8cHJlY2lzaW9uPjM2MDA8L3ByZWNpc2lvbj5cbiAgICAgICAgICAgIDwvcmV0ZW50aW9uPlxuICAgICAgICA8L2RlZmF1bHQ+XG4gICAgPC9ncmFwaGl0ZV9yb2xsdXBfZXhhbXBsZT5cblxuICAgIDwhLS0gRGlyZWN0b3J5IGluIDxjbGlja2hvdXNlLXBhdGg+IGNvbnRhaW5pbmcgc2NoZW1hIGZpbGVzIGZvciB2YXJpb3VzIGlucHV0IGZvcm1hdHMuXG4gICAgICAgIFRoZSBkaXJlY3Rvcnkgd2lsbCBiZSBjcmVhdGVkIGlmIGl0IGRvZXNuJ3QgZXhpc3QuXG4gICAgICAtLT5cbiAgICA8Zm9ybWF0X3NjaGVtYV9wYXRoPi92YXIvbGliL2NsaWNraG91c2UvZm9ybWF0X3NjaGVtYXMvPC9mb3JtYXRfc2NoZW1hX3BhdGg+XG5cbiAgICA8IS0tIERlZmF1bHQgcXVlcnkgbWFza2luZyBydWxlcywgbWF0Y2hpbmcgbGluZXMgd291bGQgYmUgcmVwbGFjZWQgd2l0aCBzb21ldGhpbmcgZWxzZSBpbiB0aGVcbiAgICBsb2dzXG4gICAgICAgIChib3RoIHRleHQgbG9ncyBhbmQgc3lzdGVtLnF1ZXJ5X2xvZykuXG4gICAgICAgIG5hbWUgLSBuYW1lIGZvciB0aGUgcnVsZSAob3B0aW9uYWwpXG4gICAgICAgIHJlZ2V4cCAtIFJFMiBjb21wYXRpYmxlIHJlZ3VsYXIgZXhwcmVzc2lvbiAobWFuZGF0b3J5KVxuICAgICAgICByZXBsYWNlIC0gc3Vic3RpdHV0aW9uIHN0cmluZyBmb3Igc2Vuc2l0aXZlIGRhdGEgKG9wdGlvbmFsLCBieSBkZWZhdWx0IC0gc2l4IGFzdGVyaXNrcylcbiAgICAtLT5cbiAgICA8cXVlcnlfbWFza2luZ19ydWxlcz5cbiAgICAgICAgPHJ1bGU+XG4gICAgICAgICAgICA8bmFtZT5oaWRlIGVuY3J5cHQvZGVjcnlwdCBhcmd1bWVudHM8L25hbWU+XG4gICAgICAgICAgICA8cmVnZXhwPigoPzphZXNfKT8oPzplbmNyeXB0fGRlY3J5cHQpKD86X215c3FsKT8pXFxzKlxcKFxccyooPzonKD86XFxcXCd8LikrJ3wuKj8pXFxzKlxcKTwvcmVnZXhwPlxuICAgICAgICAgICAgPCEtLSBvciBtb3JlIHNlY3VyZSwgYnV0IGFsc28gbW9yZSBpbnZhc2l2ZTpcbiAgICAgICAgICAgICAgICAoYWVzX1xcdyspXFxzKlxcKC4qXFwpXG4gICAgICAgICAgICAtLT5cbiAgICAgICAgICAgIDxyZXBsYWNlPlxcMSg\/Pz8pPC9yZXBsYWNlPlxuICAgICAgICA8L3J1bGU+XG4gICAgPC9xdWVyeV9tYXNraW5nX3J1bGVzPlxuXG4gICAgPCEtLSBVbmNvbW1lbnQgdG8gdXNlIGN1c3RvbSBodHRwIGhhbmRsZXJzLlxuICAgICAgICBydWxlcyBhcmUgY2hlY2tlZCBmcm9tIHRvcCB0byBib3R0b20sIGZpcnN0IG1hdGNoIHJ1bnMgdGhlIGhhbmRsZXJcbiAgICAgICAgICAgIHVybCAtIHRvIG1hdGNoIHJlcXVlc3QgVVJMLCB5b3UgY2FuIHVzZSAncmVnZXg6JyBwcmVmaXggdG8gdXNlIHJlZ2V4IG1hdGNoKG9wdGlvbmFsKVxuICAgICAgICAgICAgbWV0aG9kcyAtIHRvIG1hdGNoIHJlcXVlc3QgbWV0aG9kLCB5b3UgY2FuIHVzZSBjb21tYXMgdG8gc2VwYXJhdGUgbXVsdGlwbGUgbWV0aG9kIG1hdGNoZXMob3B0aW9uYWwpXG4gICAgICAgICAgICBoZWFkZXJzIC0gdG8gbWF0Y2ggcmVxdWVzdCBoZWFkZXJzLCBtYXRjaCBlYWNoIGNoaWxkIGVsZW1lbnQoY2hpbGQgZWxlbWVudCBuYW1lIGlzIGhlYWRlciBuYW1lKSxcbiAgICB5b3UgY2FuIHVzZSAncmVnZXg6JyBwcmVmaXggdG8gdXNlIHJlZ2V4IG1hdGNoKG9wdGlvbmFsKVxuICAgICAgICBoYW5kbGVyIGlzIHJlcXVlc3QgaGFuZGxlclxuICAgICAgICAgICAgdHlwZSAtIHN1cHBvcnRlZCB0eXBlczogc3RhdGljLCBkeW5hbWljX3F1ZXJ5X2hhbmRsZXIsIHByZWRlZmluZWRfcXVlcnlfaGFuZGxlclxuICAgICAgICAgICAgcXVlcnkgLSB1c2Ugd2l0aCBwcmVkZWZpbmVkX3F1ZXJ5X2hhbmRsZXIgdHlwZSwgZXhlY3V0ZXMgcXVlcnkgd2hlbiB0aGUgaGFuZGxlciBpcyBjYWxsZWRcbiAgICAgICAgICAgIHF1ZXJ5X3BhcmFtX25hbWUgLSB1c2Ugd2l0aCBkeW5hbWljX3F1ZXJ5X2hhbmRsZXIgdHlwZSwgZXh0cmFjdHMgYW5kIGV4ZWN1dGVzIHRoZSB2YWx1ZVxuICAgIGNvcnJlc3BvbmRpbmcgdG8gdGhlIDxxdWVyeV9wYXJhbV9uYW1lPiB2YWx1ZSBpbiBIVFRQIHJlcXVlc3QgcGFyYW1zXG4gICAgICAgICAgICBzdGF0dXMgLSB1c2Ugd2l0aCBzdGF0aWMgdHlwZSwgcmVzcG9uc2Ugc3RhdHVzIGNvZGVcbiAgICAgICAgICAgIGNvbnRlbnRfdHlwZSAtIHVzZSB3aXRoIHN0YXRpYyB0eXBlLCByZXNwb25zZSBjb250ZW50LXR5cGVcbiAgICAgICAgICAgIHJlc3BvbnNlX2NvbnRlbnQgLSB1c2Ugd2l0aCBzdGF0aWMgdHlwZSwgUmVzcG9uc2UgY29udGVudCBzZW50IHRvIGNsaWVudCwgd2hlbiB1c2luZyB0aGUgcHJlZml4XG4gICAgJ2ZpbGU6Ly8nIG9yICdjb25maWc6Ly8nLCBmaW5kIHRoZSBjb250ZW50IGZyb20gdGhlIGZpbGUgb3IgY29uZmlndXJhdGlvbiBzZW5kIHRvIGNsaWVudC5cblxuICAgIDxodHRwX2hhbmRsZXJzPlxuICAgICAgICA8cnVsZT5cbiAgICAgICAgICAgIDx1cmw+LzwvdXJsPlxuICAgICAgICAgICAgPG1ldGhvZHM+UE9TVCxHRVQ8L21ldGhvZHM+XG4gICAgICAgICAgICA8aGVhZGVycz48cHJhZ21hPm5vLWNhY2hlPC9wcmFnbWE+PC9oZWFkZXJzPlxuICAgICAgICAgICAgPGhhbmRsZXI+XG4gICAgICAgICAgICAgICAgPHR5cGU+ZHluYW1pY19xdWVyeV9oYW5kbGVyPC90eXBlPlxuICAgICAgICAgICAgICAgIDxxdWVyeV9wYXJhbV9uYW1lPnF1ZXJ5PC9xdWVyeV9wYXJhbV9uYW1lPlxuICAgICAgICAgICAgPC9oYW5kbGVyPlxuICAgICAgICA8L3J1bGU+XG5cbiAgICAgICAgPHJ1bGU+XG4gICAgICAgICAgICA8dXJsPi9wcmVkZWZpbmVkX3F1ZXJ5PC91cmw+XG4gICAgICAgICAgICA8bWV0aG9kcz5QT1NULEdFVDwvbWV0aG9kcz5cbiAgICAgICAgICAgIDxoYW5kbGVyPlxuICAgICAgICAgICAgICAgIDx0eXBlPnByZWRlZmluZWRfcXVlcnlfaGFuZGxlcjwvdHlwZT5cbiAgICAgICAgICAgICAgICA8cXVlcnk+U0VMRUNUICogRlJPTSBzeXN0ZW0uc2V0dGluZ3M8L3F1ZXJ5PlxuICAgICAgICAgICAgPC9oYW5kbGVyPlxuICAgICAgICA8L3J1bGU+XG5cbiAgICAgICAgPHJ1bGU+XG4gICAgICAgICAgICA8aGFuZGxlcj5cbiAgICAgICAgICAgICAgICA8dHlwZT5zdGF0aWM8L3R5cGU+XG4gICAgICAgICAgICAgICAgPHN0YXR1cz4yMDA8L3N0YXR1cz5cbiAgICAgICAgICAgICAgICA8Y29udGVudF90eXBlPnRleHQvcGxhaW47IGNoYXJzZXQ9VVRGLTg8L2NvbnRlbnRfdHlwZT5cbiAgICAgICAgICAgICAgICA8cmVzcG9uc2VfY29udGVudD5jb25maWc6Ly9odHRwX3NlcnZlcl9kZWZhdWx0X3Jlc3BvbnNlPC9yZXNwb25zZV9jb250ZW50PlxuICAgICAgICAgICAgPC9oYW5kbGVyPlxuICAgICAgICA8L3J1bGU+XG4gICAgPC9odHRwX2hhbmRsZXJzPlxuICAgIC0tPlxuXG4gICAgPHNlbmRfY3Jhc2hfcmVwb3J0cz5cbiAgICAgICAgPCEtLSBDaGFuZ2luZyA8ZW5hYmxlZD4gdG8gdHJ1ZSBhbGxvd3Mgc2VuZGluZyBjcmFzaCByZXBvcnRzIHRvIC0tPlxuICAgICAgICA8IS0tIHRoZSBDbGlja0hvdXNlIGNvcmUgZGV2ZWxvcGVycyB0ZWFtIHZpYSBTZW50cnkgaHR0cHM6Ly9zZW50cnkuaW8gLS0+XG4gICAgICAgIDwhLS0gRG9pbmcgc28gYXQgbGVhc3QgaW4gcHJlLXByb2R1Y3Rpb24gZW52aXJvbm1lbnRzIGlzIGhpZ2hseSBhcHByZWNpYXRlZCAtLT5cbiAgICAgICAgPGVuYWJsZWQ+ZmFsc2U8L2VuYWJsZWQ+XG4gICAgICAgIDwhLS0gQ2hhbmdlIDxhbm9ueW1pemU+IHRvIHRydWUgaWYgeW91IGRvbid0IGZlZWwgY29tZm9ydGFibGUgYXR0YWNoaW5nIHRoZSBzZXJ2ZXIgaG9zdG5hbWVcbiAgICAgICAgdG8gdGhlIGNyYXNoIHJlcG9ydCAtLT5cbiAgICAgICAgPGFub255bWl6ZT5mYWxzZTwvYW5vbnltaXplPlxuICAgICAgICA8IS0tIERlZmF1bHQgZW5kcG9pbnQgc2hvdWxkIGJlIGNoYW5nZWQgdG8gZGlmZmVyZW50IFNlbnRyeSBEU04gb25seSBpZiB5b3UgaGF2ZSAtLT5cbiAgICAgICAgPCEtLSBzb21lIGluLWhvdXNlIGVuZ2luZWVycyBvciBoaXJlZCBjb25zdWx0YW50cyB3aG8ncmUgZ29pbmcgdG8gZGVidWcgQ2xpY2tIb3VzZSBpc3N1ZXNcbiAgICAgICAgZm9yIHlvdSAtLT5cbiAgICAgICAgPGVuZHBvaW50Pmh0dHBzOi8vNmYzMzAzNGNmZTY4NGRkN2EzYWI5ODc1ZTU3YjFjOGRAbzM4ODg3MC5pbmdlc3Quc2VudHJ5LmlvLzUyMjYyNzc8L2VuZHBvaW50PlxuICAgIDwvc2VuZF9jcmFzaF9yZXBvcnRzPlxuXG4gICAgPCEtLSBVbmNvbW1lbnQgdG8gZGlzYWJsZSBDbGlja0hvdXNlIGludGVybmFsIEROUyBjYWNoaW5nLiAtLT5cbiAgICA8IS0tIDxkaXNhYmxlX2ludGVybmFsX2Ruc19jYWNoZT4xPC9kaXNhYmxlX2ludGVybmFsX2Ruc19jYWNoZT4gLS0+XG5cbiAgICA8IS0tIFlvdSBjYW4gYWxzbyBjb25maWd1cmUgcm9ja3NkYiBsaWtlIHRoaXM6IC0tPlxuICAgIDwhLS1cbiAgICA8cm9ja3NkYj5cbiAgICAgICAgPG9wdGlvbnM+XG4gICAgICAgICAgICA8bWF4X2JhY2tncm91bmRfam9icz44PC9tYXhfYmFja2dyb3VuZF9qb2JzPlxuICAgICAgICA8L29wdGlvbnM+XG4gICAgICAgIDxjb2x1bW5fZmFtaWx5X29wdGlvbnM+XG4gICAgICAgICAgICA8bnVtX2xldmVscz4yPC9udW1fbGV2ZWxzPlxuICAgICAgICA8L2NvbHVtbl9mYW1pbHlfb3B0aW9ucz5cbiAgICAgICAgPHRhYmxlcz5cbiAgICAgICAgICAgIDx0YWJsZT5cbiAgICAgICAgICAgICAgICA8bmFtZT5UQUJMRTwvbmFtZT5cbiAgICAgICAgICAgICAgICA8b3B0aW9ucz5cbiAgICAgICAgICAgICAgICAgICAgPG1heF9iYWNrZ3JvdW5kX2pvYnM+ODwvbWF4X2JhY2tncm91bmRfam9icz5cbiAgICAgICAgICAgICAgICA8L29wdGlvbnM+XG4gICAgICAgICAgICAgICAgPGNvbHVtbl9mYW1pbHlfb3B0aW9ucz5cbiAgICAgICAgICAgICAgICAgICAgPG51bV9sZXZlbHM+MjwvbnVtX2xldmVscz5cbiAgICAgICAgICAgICAgICA8L2NvbHVtbl9mYW1pbHlfb3B0aW9ucz5cbiAgICAgICAgICAgIDwvdGFibGU+XG4gICAgICAgIDwvdGFibGVzPlxuICAgIDwvcm9ja3NkYj5cbiAgICAtLT5cbjwveWFuZGV4PiIKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vZG9ja2VyL2NsaWNraG91c2UvdXNlcnMueG1sCiAgICAgICAgdGFyZ2V0OiAvZXRjL2NsaWNraG91c2Utc2VydmVyL3VzZXJzLnhtbAogICAgICAgIGNvbnRlbnQ6ICI8P3htbCB2ZXJzaW9uPVwiMS4wXCI\/PlxuPHlhbmRleD5cbiAgICA8IS0tIFNlZSBhbHNvIHRoZSBmaWxlcyBpbiB1c2Vycy5kIGRpcmVjdG9yeSB3aGVyZSB0aGUgc2V0dGluZ3MgY2FuIGJlIG92ZXJyaWRkZW4uIC0tPlxuXG4gICAgPCEtLSBQcm9maWxlcyBvZiBzZXR0aW5ncy4gLS0+XG4gICAgPHByb2ZpbGVzPlxuICAgICAgICA8IS0tIERlZmF1bHQgc2V0dGluZ3MuIC0tPlxuICAgICAgICA8ZGVmYXVsdD5cbiAgICAgICAgICAgIDwhLS0gTWF4aW11bSBtZW1vcnkgdXNhZ2UgZm9yIHByb2Nlc3Npbmcgc2luZ2xlIHF1ZXJ5LCBpbiBieXRlcy4gLS0+XG4gICAgICAgICAgICA8bWF4X21lbW9yeV91c2FnZT4xMDAwMDAwMDAwMDwvbWF4X21lbW9yeV91c2FnZT5cblxuICAgICAgICAgICAgPCEtLSBIb3cgdG8gY2hvb3NlIGJldHdlZW4gcmVwbGljYXMgZHVyaW5nIGRpc3RyaWJ1dGVkIHF1ZXJ5IHByb2Nlc3NpbmcuXG4gICAgICAgICAgICAgICAgcmFuZG9tIC0gY2hvb3NlIHJhbmRvbSByZXBsaWNhIGZyb20gc2V0IG9mIHJlcGxpY2FzIHdpdGggbWluaW11bSBudW1iZXIgb2YgZXJyb3JzXG4gICAgICAgICAgICAgICAgbmVhcmVzdF9ob3N0bmFtZSAtIGZyb20gc2V0IG9mIHJlcGxpY2FzIHdpdGggbWluaW11bSBudW1iZXIgb2YgZXJyb3JzLCBjaG9vc2UgcmVwbGljYVxuICAgICAgICAgICAgICAgICAgd2l0aCBtaW5pbXVtIG51bWJlciBvZiBkaWZmZXJlbnQgc3ltYm9scyBiZXR3ZWVuIHJlcGxpY2EncyBob3N0bmFtZSBhbmQgbG9jYWwgaG9zdG5hbWVcbiAgICAgICAgICAgICAgICAgIChIYW1taW5nIGRpc3RhbmNlKS5cbiAgICAgICAgICAgICAgICBpbl9vcmRlciAtIGZpcnN0IGxpdmUgcmVwbGljYSBpcyBjaG9zZW4gaW4gc3BlY2lmaWVkIG9yZGVyLlxuICAgICAgICAgICAgICAgIGZpcnN0X29yX3JhbmRvbSAtIGlmIGZpcnN0IHJlcGxpY2Egb25lIGhhcyBoaWdoZXIgbnVtYmVyIG9mIGVycm9ycywgcGljayBhIHJhbmRvbSBvbmUgZnJvbSByZXBsaWNhc1xuICAgICAgICAgICAgd2l0aCBtaW5pbXVtIG51bWJlciBvZiBlcnJvcnMuXG4gICAgICAgICAgICAtLT5cbiAgICAgICAgICAgIDxsb2FkX2JhbGFuY2luZz5yYW5kb208L2xvYWRfYmFsYW5jaW5nPlxuXG4gICAgICAgICAgICA8YWxsb3dfbm9uZGV0ZXJtaW5pc3RpY19tdXRhdGlvbnM+MTwvYWxsb3dfbm9uZGV0ZXJtaW5pc3RpY19tdXRhdGlvbnM+XG5cbiAgICAgICAgPC9kZWZhdWx0PlxuXG4gICAgICAgIDwhLS0gUHJvZmlsZSB0aGF0IGFsbG93cyBvbmx5IHJlYWQgcXVlcmllcy4gLS0+XG4gICAgICAgIDxyZWFkb25seT5cbiAgICAgICAgICAgIDxyZWFkb25seT4xPC9yZWFkb25seT5cbiAgICAgICAgPC9yZWFkb25seT5cblxuICAgIDwvcHJvZmlsZXM+XG5cbiAgICA8IS0tIFVzZXJzIGFuZCBBQ0wuIC0tPlxuICAgIDx1c2Vycz5cbiAgICAgICAgPCEtLSBJZiB1c2VyIG5hbWUgd2FzIG5vdCBzcGVjaWZpZWQsICdkZWZhdWx0JyB1c2VyIGlzIHVzZWQuIC0tPlxuICAgICAgICA8ZGVmYXVsdD5cbiAgICAgICAgICAgIDwhLS0gU2VlIGFsc28gdGhlIGZpbGVzIGluIHVzZXJzLmQgZGlyZWN0b3J5IHdoZXJlIHRoZSBwYXNzd29yZCBjYW4gYmUgb3ZlcnJpZGRlbi5cblxuICAgICAgICAgICAgICAgIFBhc3N3b3JkIGNvdWxkIGJlIHNwZWNpZmllZCBpbiBwbGFpbnRleHQgb3IgaW4gU0hBMjU2IChpbiBoZXggZm9ybWF0KS5cblxuICAgICAgICAgICAgICAgIElmIHlvdSB3YW50IHRvIHNwZWNpZnkgcGFzc3dvcmQgaW4gcGxhaW50ZXh0IChub3QgcmVjb21tZW5kZWQpLCBwbGFjZSBpdCBpbiAncGFzc3dvcmQnIGVsZW1lbnQuXG4gICAgICAgICAgICAgICAgRXhhbXBsZTogPHBhc3N3b3JkPnF3ZXJ0eTwvcGFzc3dvcmQ+LlxuICAgICAgICAgICAgICAgIFBhc3N3b3JkIGNvdWxkIGJlIGVtcHR5LlxuXG4gICAgICAgICAgICAgICAgSWYgeW91IHdhbnQgdG8gc3BlY2lmeSBTSEEyNTYsIHBsYWNlIGl0IGluICdwYXNzd29yZF9zaGEyNTZfaGV4JyBlbGVtZW50LlxuICAgICAgICAgICAgICAgIEV4YW1wbGU6XG4gICAgICAgICAgICA8cGFzc3dvcmRfc2hhMjU2X2hleD42NWU4NGJlMzM1MzJmYjc4NGM0ODEyOTY3NWY5ZWZmM2E2ODJiMjcxNjhjMGVhNzQ0YjJjZjU4ZWUwMjMzN2M1PC9wYXNzd29yZF9zaGEyNTZfaGV4PlxuICAgICAgICAgICAgICAgIFJlc3RyaWN0aW9ucyBvZiBTSEEyNTY6IGltcG9zc2liaWxpdHkgdG8gY29ubmVjdCB0byBDbGlja0hvdXNlIHVzaW5nIE15U1FMIEpTIGNsaWVudCAoYXMgb2YgSnVseVxuICAgICAgICAgICAgMjAxOSkuXG5cbiAgICAgICAgICAgICAgICBJZiB5b3Ugd2FudCB0byBzcGVjaWZ5IGRvdWJsZSBTSEExLCBwbGFjZSBpdCBpbiAncGFzc3dvcmRfZG91YmxlX3NoYTFfaGV4JyBlbGVtZW50LlxuICAgICAgICAgICAgICAgIEV4YW1wbGU6XG4gICAgICAgICAgICA8cGFzc3dvcmRfZG91YmxlX3NoYTFfaGV4PmUzOTU3OTZkNjU0NmIxYjY1ZGI5ZDY2NWNkNDNmMGU4NThkZDQzMDM8L3Bhc3N3b3JkX2RvdWJsZV9zaGExX2hleD5cblxuICAgICAgICAgICAgICAgIElmIHlvdSB3YW50IHRvIHNwZWNpZnkgYSBwcmV2aW91c2x5IGRlZmluZWQgTERBUCBzZXJ2ZXIgKHNlZSAnbGRhcF9zZXJ2ZXJzJyBpbiB0aGUgbWFpbiBjb25maWcpIGZvclxuICAgICAgICAgICAgYXV0aGVudGljYXRpb24sXG4gICAgICAgICAgICAgICAgICBwbGFjZSBpdHMgbmFtZSBpbiAnc2VydmVyJyBlbGVtZW50IGluc2lkZSAnbGRhcCcgZWxlbWVudC5cbiAgICAgICAgICAgICAgICBFeGFtcGxlOiA8bGRhcD48c2VydmVyPm15X2xkYXBfc2VydmVyPC9zZXJ2ZXI+PC9sZGFwPlxuXG4gICAgICAgICAgICAgICAgSWYgeW91IHdhbnQgdG8gYXV0aGVudGljYXRlIHRoZSB1c2VyIHZpYSBLZXJiZXJvcyAoYXNzdW1pbmcgS2VyYmVyb3MgaXMgZW5hYmxlZCwgc2VlICdrZXJiZXJvcycgaW5cbiAgICAgICAgICAgIHRoZSBtYWluIGNvbmZpZyksXG4gICAgICAgICAgICAgICAgICBwbGFjZSAna2VyYmVyb3MnIGVsZW1lbnQgaW5zdGVhZCBvZiAncGFzc3dvcmQnIChhbmQgc2ltaWxhcikgZWxlbWVudHMuXG4gICAgICAgICAgICAgICAgVGhlIG5hbWUgcGFydCBvZiB0aGUgY2Fub25pY2FsIHByaW5jaXBhbCBuYW1lIG9mIHRoZSBpbml0aWF0b3IgbXVzdCBtYXRjaCB0aGUgdXNlciBuYW1lIGZvclxuICAgICAgICAgICAgYXV0aGVudGljYXRpb24gdG8gc3VjY2VlZC5cbiAgICAgICAgICAgICAgICBZb3UgY2FuIGFsc28gcGxhY2UgJ3JlYWxtJyBlbGVtZW50IGluc2lkZSAna2VyYmVyb3MnIGVsZW1lbnQgdG8gZnVydGhlciByZXN0cmljdCBhdXRoZW50aWNhdGlvbiB0b1xuICAgICAgICAgICAgb25seSB0aG9zZSByZXF1ZXN0c1xuICAgICAgICAgICAgICAgICAgd2hvc2UgaW5pdGlhdG9yJ3MgcmVhbG0gbWF0Y2hlcyBpdC5cbiAgICAgICAgICAgICAgICBFeGFtcGxlOiA8a2VyYmVyb3MgLz5cbiAgICAgICAgICAgICAgICBFeGFtcGxlOiA8a2VyYmVyb3M+PHJlYWxtPkVYQU1QTEUuQ09NPC9yZWFsbT48L2tlcmJlcm9zPlxuXG4gICAgICAgICAgICAgICAgSG93IHRvIGdlbmVyYXRlIGRlY2VudCBwYXNzd29yZDpcbiAgICAgICAgICAgICAgICBFeGVjdXRlOiBQQVNTV09SRD0kKGJhc2U2NCA8IC9kZXYvdXJhbmRvbSB8IGhlYWQgLWM4KTsgZWNobyBcIiRQQVNTV09SRFwiOyBlY2hvIC1uIFwiJFBBU1NXT1JEXCIgfFxuICAgICAgICAgICAgc2hhMjU2c3VtIHwgdHIgLWQgJy0nXG4gICAgICAgICAgICAgICAgSW4gZmlyc3QgbGluZSB3aWxsIGJlIHBhc3N3b3JkIGFuZCBpbiBzZWNvbmQgLSBjb3JyZXNwb25kaW5nIFNIQTI1Ni5cblxuICAgICAgICAgICAgICAgIEhvdyB0byBnZW5lcmF0ZSBkb3VibGUgU0hBMTpcbiAgICAgICAgICAgICAgICBFeGVjdXRlOiBQQVNTV09SRD0kKGJhc2U2NCA8IC9kZXYvdXJhbmRvbSB8IGhlYWQgLWM4KTsgZWNobyBcIiRQQVNTV09SRFwiOyBlY2hvIC1uIFwiJFBBU1NXT1JEXCIgfFxuICAgICAgICAgICAgc2hhMXN1bSB8IHRyIC1kICctJyB8IHh4ZCAtciAtcCB8IHNoYTFzdW0gfCB0ciAtZCAnLSdcbiAgICAgICAgICAgICAgICBJbiBmaXJzdCBsaW5lIHdpbGwgYmUgcGFzc3dvcmQgYW5kIGluIHNlY29uZCAtIGNvcnJlc3BvbmRpbmcgZG91YmxlIFNIQTEuXG4gICAgICAgICAgICAtLT5cbiAgICAgICAgICAgIDxwYXNzd29yZD48L3Bhc3N3b3JkPlxuXG4gICAgICAgICAgICA8IS0tIExpc3Qgb2YgbmV0d29ya3Mgd2l0aCBvcGVuIGFjY2Vzcy5cblxuICAgICAgICAgICAgICAgIFRvIG9wZW4gYWNjZXNzIGZyb20gZXZlcnl3aGVyZSwgc3BlY2lmeTpcbiAgICAgICAgICAgICAgICAgICAgPGlwPjo6LzA8L2lwPlxuXG4gICAgICAgICAgICAgICAgVG8gb3BlbiBhY2Nlc3Mgb25seSBmcm9tIGxvY2FsaG9zdCwgc3BlY2lmeTpcbiAgICAgICAgICAgICAgICAgICAgPGlwPjo6MTwvaXA+XG4gICAgICAgICAgICAgICAgICAgIDxpcD4xMjcuMC4wLjE8L2lwPlxuXG4gICAgICAgICAgICAgICAgRWFjaCBlbGVtZW50IG9mIGxpc3QgaGFzIG9uZSBvZiB0aGUgZm9sbG93aW5nIGZvcm1zOlxuICAgICAgICAgICAgICAgIDxpcD4gSVAtYWRkcmVzcyBvciBuZXR3b3JrIG1hc2suIEV4YW1wbGVzOiAyMTMuMTgwLjIwNC4zIG9yIDEwLjAuMC4xLzggb3IgMTAuMC4wLjEvMjU1LjI1NS4yNTUuMFxuICAgICAgICAgICAgICAgICAgICAyYTAyOjZiODo6MyBvciAyYTAyOjZiODo6My82NCBvciAyYTAyOjZiODo6My9mZmZmOmZmZmY6ZmZmZjpmZmZmOjouXG4gICAgICAgICAgICAgICAgPGhvc3Q+IEhvc3RuYW1lLiBFeGFtcGxlOiBzZXJ2ZXIwMS55YW5kZXgucnUuXG4gICAgICAgICAgICAgICAgICAgIFRvIGNoZWNrIGFjY2VzcywgRE5TIHF1ZXJ5IGlzIHBlcmZvcm1lZCwgYW5kIGFsbCByZWNlaXZlZCBhZGRyZXNzZXMgY29tcGFyZWQgdG8gcGVlciBhZGRyZXNzLlxuICAgICAgICAgICAgICAgIDxob3N0X3JlZ2V4cD4gUmVndWxhciBleHByZXNzaW9uIGZvciBob3N0IG5hbWVzLiBFeGFtcGxlLCBec2VydmVyXFxkXFxkLVxcZFxcZC1cXGRcXC55YW5kZXhcXC5ydSRcbiAgICAgICAgICAgICAgICAgICAgVG8gY2hlY2sgYWNjZXNzLCBETlMgUFRSIHF1ZXJ5IGlzIHBlcmZvcm1lZCBmb3IgcGVlciBhZGRyZXNzIGFuZCB0aGVuIHJlZ2V4cCBpcyBhcHBsaWVkLlxuICAgICAgICAgICAgICAgICAgICBUaGVuLCBmb3IgcmVzdWx0IG9mIFBUUiBxdWVyeSwgYW5vdGhlciBETlMgcXVlcnkgaXMgcGVyZm9ybWVkIGFuZCBhbGwgcmVjZWl2ZWQgYWRkcmVzc2VzIGNvbXBhcmVkXG4gICAgICAgICAgICB0byBwZWVyIGFkZHJlc3MuXG4gICAgICAgICAgICAgICAgICAgIFN0cm9uZ2x5IHJlY29tbWVuZGVkIHRoYXQgcmVnZXhwIGlzIGVuZHMgd2l0aCAkXG4gICAgICAgICAgICAgICAgQWxsIHJlc3VsdHMgb2YgRE5TIHJlcXVlc3RzIGFyZSBjYWNoZWQgdGlsbCBzZXJ2ZXIgcmVzdGFydC5cbiAgICAgICAgICAgIC0tPlxuICAgICAgICAgICAgPG5ldHdvcmtzPlxuICAgICAgICAgICAgICAgIDxpcD46Oi8wPC9pcD5cbiAgICAgICAgICAgIDwvbmV0d29ya3M+XG5cbiAgICAgICAgICAgIDwhLS0gU2V0dGluZ3MgcHJvZmlsZSBmb3IgdXNlci4gLS0+XG4gICAgICAgICAgICA8cHJvZmlsZT5kZWZhdWx0PC9wcm9maWxlPlxuXG4gICAgICAgICAgICA8IS0tIFF1b3RhIGZvciB1c2VyLiAtLT5cbiAgICAgICAgICAgIDxxdW90YT5kZWZhdWx0PC9xdW90YT5cblxuICAgICAgICAgICAgPCEtLSBVc2VyIGNhbiBjcmVhdGUgb3RoZXIgdXNlcnMgYW5kIGdyYW50IHJpZ2h0cyB0byB0aGVtLiAtLT5cbiAgICAgICAgICAgIDwhLS0gPGFjY2Vzc19tYW5hZ2VtZW50PjE8L2FjY2Vzc19tYW5hZ2VtZW50PiAtLT5cbiAgICAgICAgPC9kZWZhdWx0PlxuICAgIDwvdXNlcnM+XG5cbiAgICA8IS0tIFF1b3Rhcy4gLS0+XG4gICAgPHF1b3Rhcz5cbiAgICAgICAgPCEtLSBOYW1lIG9mIHF1b3RhLiAtLT5cbiAgICAgICAgPGRlZmF1bHQ+XG4gICAgICAgICAgICA8IS0tIExpbWl0cyBmb3IgdGltZSBpbnRlcnZhbC4gWW91IGNvdWxkIHNwZWNpZnkgbWFueSBpbnRlcnZhbHMgd2l0aCBkaWZmZXJlbnQgbGltaXRzLiAtLT5cbiAgICAgICAgICAgIDxpbnRlcnZhbD5cbiAgICAgICAgICAgICAgICA8IS0tIExlbmd0aCBvZiBpbnRlcnZhbC4gLS0+XG4gICAgICAgICAgICAgICAgPGR1cmF0aW9uPjM2MDA8L2R1cmF0aW9uPlxuXG4gICAgICAgICAgICAgICAgPCEtLSBObyBsaW1pdHMuIEp1c3QgY2FsY3VsYXRlIHJlc291cmNlIHVzYWdlIGZvciB0aW1lIGludGVydmFsLiAtLT5cbiAgICAgICAgICAgICAgICA8cXVlcmllcz4wPC9xdWVyaWVzPlxuICAgICAgICAgICAgICAgIDxlcnJvcnM+MDwvZXJyb3JzPlxuICAgICAgICAgICAgICAgIDxyZXN1bHRfcm93cz4wPC9yZXN1bHRfcm93cz5cbiAgICAgICAgICAgICAgICA8cmVhZF9yb3dzPjA8L3JlYWRfcm93cz5cbiAgICAgICAgICAgICAgICA8ZXhlY3V0aW9uX3RpbWU+MDwvZXhlY3V0aW9uX3RpbWU+XG4gICAgICAgICAgICA8L2ludGVydmFsPlxuICAgICAgICA8L2RlZmF1bHQ+XG4gICAgPC9xdW90YXM+XG48L3lhbmRleD5cbiIKICAgICAgLSAnY2xpY2tob3VzZS1kYXRhOi92YXIvbGliL2NsaWNraG91c2UnCiAgICBkZXBlbmRzX29uOgogICAgICAtIGthZmthCiAgICAgIC0gem9va2VlcGVyCiAgem9va2VlcGVyOgogICAgaW1hZ2U6ICd6b29rZWVwZXI6My43LjAnCiAgICB2b2x1bWVzOgogICAgICAtICd6b29rZWVwZXItZGF0YWxvZzovZGF0YWxvZycKICAgICAgLSAnem9va2VlcGVyLWRhdGE6L2RhdGEnCiAgICAgIC0gJ3pvb2tlZXBlci1sb2dzOi9sb2dzJwogIGthZmthOgogICAgaW1hZ2U6ICdnaGNyLmlvL3Bvc3Rob2cva2Fma2EtY29udGFpbmVyOnYyLjguMicKICAgIGRlcGVuZHNfb246CiAgICAgIC0gem9va2VlcGVyCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBLQUZLQV9CUk9LRVJfSUQ9MTAwMQogICAgICAtIEtBRktBX0NGR19SRVNFUlZFRF9CUk9LRVJfTUFYX0lEPTEwMDEKICAgICAgLSAnS0FGS0FfQ0ZHX0xJU1RFTkVSUz1QTEFJTlRFWFQ6Ly86OTA5MicKICAgICAgLSAnS0FGS0FfQ0ZHX0FEVkVSVElTRURfTElTVEVORVJTPVBMQUlOVEVYVDovL2thZmthOjkwOTInCiAgICAgIC0gJ0tBRktBX0NGR19aT09LRUVQRVJfQ09OTkVDVD16b29rZWVwZXI6MjE4MScKICAgICAgLSBBTExPV19QTEFJTlRFWFRfTElTVEVORVI9eWVzCiAgb2JqZWN0X3N0b3JhZ2U6CiAgICBpbWFnZTogJ21pbmlvL21pbmlvOlJFTEVBU0UuMjAyMi0wNi0yNVQxNS01MC0xNlonCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBNSU5JT19ST09UX1VTRVI9JFNFUlZJQ0VfVVNFUl9NSU5JTwogICAgICAtIE1JTklPX1JPT1RfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfTUlOSU8KICAgIGVudHJ5cG9pbnQ6IHNoCiAgICBjb21tYW5kOiAnLWMgJydta2RpciAtcCAvZGF0YS9wb3N0aG9nICYmIG1pbmlvIHNlcnZlciAtLWFkZHJlc3MgIjoxOTAwMCIgLS1jb25zb2xlLWFkZHJlc3MgIjoxOTAwMSIgL2RhdGEnJycKICAgIHZvbHVtZXM6CiAgICAgIC0gJ29iamVjdF9zdG9yYWdlOi9kYXRhJwogIG1haWxkZXY6CiAgICBpbWFnZTogJ21haWxkZXYvbWFpbGRldjoyLjAuNScKICBmbG93ZXI6CiAgICBpbWFnZTogJ21oZXIvZmxvd2VyOjIuMC4wJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIEZMT1dFUl9QT1JUOiA1NTU1CiAgICAgIENFTEVSWV9CUk9LRVJfVVJMOiAncmVkaXM6Ly9yZWRpczo2Mzc5JwogIHdlYjoKICAgIGltYWdlOiAncG9zdGhvZy9wb3N0aG9nOmxhdGVzdCcKICAgIGNvbW1hbmQ6IC9jb21wb3NlL3N0YXJ0CiAgICB2b2x1bWVzOgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9jb21wb3NlL3N0YXJ0CiAgICAgICAgdGFyZ2V0OiAvY29tcG9zZS9zdGFydAogICAgICAgIGNvbnRlbnQ6ICIjIS9iaW4vYmFzaFxuL2NvbXBvc2Uvd2FpdFxuLi9iaW4vbWlncmF0ZVxuLi9iaW4vZG9ja2VyLXNlcnZlclxuIgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9jb21wb3NlL3dhaXQKICAgICAgICB0YXJnZXQ6IC9jb21wb3NlL3dhaXQKICAgICAgICBjb250ZW50OiAiIyEvdXNyL2Jpbi9lbnYgcHl0aG9uM1xuXG5pbXBvcnQgc29ja2V0XG5pbXBvcnQgdGltZVxuXG5kZWYgbG9vcCgpOlxuICAgIHByaW50KFwiV2FpdGluZyBmb3IgQ2xpY2tIb3VzZSBhbmQgUG9zdGdyZXMgdG8gYmUgcmVhZHlcIilcbiAgICB0cnk6XG4gICAgICAgIHdpdGggc29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCwgc29ja2V0LlNPQ0tfU1RSRUFNKSBhcyBzOlxuICAgICAgICAgICAgcy5jb25uZWN0KCgnY2xpY2tob3VzZScsIDkwMDApKVxuICAgICAgICBwcmludChcIkNsaWNraG91c2UgaXMgcmVhZHlcIilcbiAgICAgICAgd2l0aCBzb2NrZXQuc29ja2V0KHNvY2tldC5BRl9JTkVULCBzb2NrZXQuU09DS19TVFJFQU0pIGFzIHM6XG4gICAgICAgICAgICBzLmNvbm5lY3QoKCdkYicsIDU0MzIpKVxuICAgICAgICBwcmludChcIlBvc3RncmVzIGlzIHJlYWR5XCIpXG4gICAgZXhjZXB0IENvbm5lY3Rpb25SZWZ1c2VkRXJyb3IgYXMgZTpcbiAgICAgICAgdGltZS5zbGVlcCg1KVxuICAgICAgICBsb29wKClcblxubG9vcCgpXG4iCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fV0VCXzgwMDAKICAgICAgLSBPUFRfT1VUX0NBUFRVUklORz10cnVlCiAgICAgIC0gRElTQUJMRV9TRUNVUkVfU1NMX1JFRElSRUNUPXRydWUKICAgICAgLSBJU19CRUhJTkRfUFJPWFk9dHJ1ZQogICAgICAtIFRSVVNUX0FMTF9QUk9YSUVTPXRydWUKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vcG9zdGhvZzokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0BkYjo1NDMyL3Bvc3Rob2cnCiAgICAgIC0gQ0xJQ0tIT1VTRV9IT1NUPWNsaWNraG91c2UKICAgICAgLSBDTElDS0hPVVNFX0RBVEFCQVNFPXBvc3Rob2cKICAgICAgLSBDTElDS0hPVVNFX1NFQ1VSRT1mYWxzZQogICAgICAtIENMSUNLSE9VU0VfVkVSSUZZPWZhbHNlCiAgICAgIC0gS0FGS0FfSE9TVFM9a2Fma2EKICAgICAgLSAnUkVESVNfVVJMPXJlZGlzOi8vcmVkaXM6NjM3OS8nCiAgICAgIC0gUEdIT1NUPWRiCiAgICAgIC0gUEdVU0VSPXBvc3Rob2cKICAgICAgLSBQR1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gREVQTE9ZTUVOVD1ob2JieQogICAgICAtIFNJVEVfVVJMPSRTRVJWSUNFX0ZRRE5fV0VCCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfU0VDUkVUS0VZCiAgICBkZXBlbmRzX29uOgogICAgICAtIGRiCiAgICAgIC0gcmVkaXMKICAgICAgLSBjbGlja2hvdXNlCiAgICAgIC0ga2Fma2EKICAgICAgLSBvYmplY3Rfc3RvcmFnZQogIHdvcmtlcjoKICAgIGltYWdlOiAncG9zdGhvZy9wb3N0aG9nOmxhdGVzdCcKICAgIGNvbW1hbmQ6ICcuL2Jpbi9kb2NrZXItd29ya2VyLWNlbGVyeSAtLXdpdGgtc2NoZWR1bGVyJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gT1BUX09VVF9DQVBUVVJJTkc9dHJ1ZQogICAgICAtIERJU0FCTEVfU0VDVVJFX1NTTF9SRURJUkVDVD10cnVlCiAgICAgIC0gSVNfQkVISU5EX1BST1hZPXRydWUKICAgICAgLSBUUlVTVF9BTExfUFJPWElFUz10cnVlCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovL3Bvc3Rob2c6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNAZGI6NTQzMi9wb3N0aG9nJwogICAgICAtIENMSUNLSE9VU0VfSE9TVD1jbGlja2hvdXNlCiAgICAgIC0gQ0xJQ0tIT1VTRV9EQVRBQkFTRT1wb3N0aG9nCiAgICAgIC0gQ0xJQ0tIT1VTRV9TRUNVUkU9ZmFsc2UKICAgICAgLSBDTElDS0hPVVNFX1ZFUklGWT1mYWxzZQogICAgICAtIEtBRktBX0hPU1RTPWthZmthCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovL3JlZGlzOjYzNzkvJwogICAgICAtIFBHSE9TVD1kYgogICAgICAtIFBHVVNFUj1wb3N0aG9nCiAgICAgIC0gUEdQQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtIERFUExPWU1FTlQ9aG9iYnkKICAgICAgLSBTSVRFX1VSTD0kU0VSVklDRV9GUUROX1dFQgogICAgICAtIFNFQ1JFVF9LRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X1NFQ1JFVEtFWQogICAgZGVwZW5kc19vbjoKICAgICAgLSBkYgogICAgICAtIHJlZGlzCiAgICAgIC0gY2xpY2tob3VzZQogICAgICAtIGthZmthCiAgICAgIC0gb2JqZWN0X3N0b3JhZ2UKICBwbHVnaW5zOgogICAgaW1hZ2U6ICdwb3N0aG9nL3Bvc3Rob2c6bGF0ZXN0JwogICAgY29tbWFuZDogJy4vYmluL3BsdWdpbi1zZXJ2ZXIgLS1uby1yZXN0YXJ0LWxvb3AnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vcG9zdGhvZzokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0BkYjo1NDMyL3Bvc3Rob2cnCiAgICAgIC0gJ0tBRktBX0hPU1RTPWthZmthOjkwOTInCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovL3JlZGlzOjYzNzkvJwogICAgICAtIENMSUNLSE9VU0VfSE9TVD1jbGlja2hvdXNlCiAgICAgIC0gQ0xJQ0tIT1VTRV9EQVRBQkFTRT1wb3N0aG9nCiAgICAgIC0gQ0xJQ0tIT1VTRV9TRUNVUkU9ZmFsc2UKICAgICAgLSBDTElDS0hPVVNFX1ZFUklGWT1mYWxzZQogICAgICAtIFNJVEVfVVJMPSRTRVJWSUNFX0ZRRE5fV0VCCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfU0VDUkVUS0VZCiAgICBkZXBlbmRzX29uOgogICAgICAtIGRiCiAgICAgIC0gcmVkaXMKICAgICAgLSBjbGlja2hvdXNlCiAgICAgIC0ga2Fma2EKICAgICAgLSBvYmplY3Rfc3RvcmFnZQogIGVsYXN0aWNzZWFyY2g6CiAgICBpbWFnZTogJ2VsYXN0aWNzZWFyY2g6Ny4xNi4yJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gY2x1c3Rlci5yb3V0aW5nLmFsbG9jYXRpb24uZGlzay50aHJlc2hvbGRfZW5hYmxlZD10cnVlCiAgICAgIC0gY2x1c3Rlci5yb3V0aW5nLmFsbG9jYXRpb24uZGlzay53YXRlcm1hcmsubG93PTUxMm1iCiAgICAgIC0gY2x1c3Rlci5yb3V0aW5nLmFsbG9jYXRpb24uZGlzay53YXRlcm1hcmsuaGlnaD0yNTZtYgogICAgICAtIGNsdXN0ZXIucm91dGluZy5hbGxvY2F0aW9uLmRpc2sud2F0ZXJtYXJrLmZsb29kX3N0YWdlPTEyOG1iCiAgICAgIC0gZGlzY292ZXJ5LnR5cGU9c2luZ2xlLW5vZGUKICAgICAgLSAnRVNfSkFWQV9PUFRTPS1YbXMyNTZtIC1YbXgyNTZtJwogICAgICAtIHhwYWNrLnNlY3VyaXR5LmVuYWJsZWQ9ZmFsc2UKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2VsYXN0aWNzZWFyY2gtZGF0YTovdmFyL2xpYi9lbGFzdGljc2VhcmNoL2RhdGEnCiAgdGVtcG9yYWw6CiAgICBpbWFnZTogJ3RlbXBvcmFsaW8vYXV0by1zZXR1cDoxLjIwLjAnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBEQj1wb3N0Z3Jlc3FsCiAgICAgIC0gREJfUE9SVD01NDMyCiAgICAgIC0gUE9TVEdSRVNfVVNFUj1wb3N0aG9nCiAgICAgIC0gUE9TVEdSRVNfUFdEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfU0VFRFM9ZGIKICAgICAgLSBEWU5BTUlDX0NPTkZJR19GSUxFX1BBVEg9Y29uZmlnL2R5bmFtaWNjb25maWcvZGV2ZWxvcG1lbnQtc3FsLnlhbWwKICAgICAgLSBFTkFCTEVfRVM9dHJ1ZQogICAgICAtIEVTX1NFRURTPWVsYXN0aWNzZWFyY2gKICAgICAgLSBFU19WRVJTSU9OPXY3CiAgICAgIC0gRU5BQkxFX0VTPWZhbHNlCiAgICBkZXBlbmRzX29uOgogICAgICBkYjoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgdm9sdW1lczoKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vZG9ja2VyL3RlbXBvcmFsL2R5bmFtaWNjb25maWcvZGV2ZWxvcG1lbnQtc3FsLnlhbWwKICAgICAgICB0YXJnZXQ6IC9ldGMvdGVtcG9yYWwvY29uZmlnL2R5bmFtaWNjb25maWcvZGV2ZWxvcG1lbnQtc3FsLnlhbWwKICAgICAgICBjb250ZW50OiAibGltaXQubWF4SURMZW5ndGg6XG4gICAgLSB2YWx1ZTogMjU1XG4gICAgICBjb25zdHJhaW50czoge31cbnN5c3RlbS5mb3JjZVNlYXJjaEF0dHJpYnV0ZXNDYWNoZVJlZnJlc2hPblJlYWQ6XG4gICAgLSB2YWx1ZTogZmFsc2VcbiAgICAgIGNvbnN0cmFpbnRzOiB7fVxuIgogIHRlbXBvcmFsLWFkbWluLXRvb2xzOgogICAgaW1hZ2U6ICd0ZW1wb3JhbGlvL2FkbWluLXRvb2xzOjEuMjAuMCcKICAgIGRlcGVuZHNfb246CiAgICAgIC0gdGVtcG9yYWwKICAgIGVudmlyb25tZW50OgogICAgICAtICdURU1QT1JBTF9DTElfQUREUkVTUz10ZW1wb3JhbDo3MjMzJwogICAgc3RkaW5fb3BlbjogdHJ1ZQogICAgdHR5OiB0cnVlCiAgdGVtcG9yYWwtdWk6CiAgICBpbWFnZTogJ3RlbXBvcmFsaW8vdWk6Mi4xMC4zJwogICAgZGVwZW5kc19vbjoKICAgICAgLSB0ZW1wb3JhbAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1RFTVBPUkFMX0FERFJFU1M9dGVtcG9yYWw6NzIzMycKICAgICAgLSAnVEVNUE9SQUxfQ09SU19PUklHSU5TPWh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCcKICB0ZW1wb3JhbC1kamFuZ28td29ya2VyOgogICAgaW1hZ2U6ICdwb3N0aG9nL3Bvc3Rob2c6bGF0ZXN0JwogICAgY29tbWFuZDogLi9iaW4vdGVtcG9yYWwtZGphbmdvLXdvcmtlcgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gRElTQUJMRV9TRUNVUkVfU1NMX1JFRElSRUNUPXRydWUKICAgICAgLSBJU19CRUhJTkRfUFJPWFk9dHJ1ZQogICAgICAtIFRSVVNUX0FMTF9QUk9YSUVTPXRydWUKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vcG9zdGhvZzokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0BkYjo1NDMyL3Bvc3Rob2cnCiAgICAgIC0gQ0xJQ0tIT1VTRV9IT1NUPWNsaWNraG91c2UKICAgICAgLSBDTElDS0hPVVNFX0RBVEFCQVNFPXBvc3Rob2cKICAgICAgLSBDTElDS0hPVVNFX1NFQ1VSRT1mYWxzZQogICAgICAtIENMSUNLSE9VU0VfVkVSSUZZPWZhbHNlCiAgICAgIC0gS0FGS0FfSE9TVFM9a2Fma2EKICAgICAgLSAnUkVESVNfVVJMPXJlZGlzOi8vcmVkaXM6NjM3OS8nCiAgICAgIC0gUEdIT1NUPWRiCiAgICAgIC0gUEdVU0VSPXBvc3Rob2cKICAgICAgLSBQR1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gREVQTE9ZTUVOVD1ob2JieQogICAgICAtIFNJVEVfVVJMPSRTRVJWSUNFX0ZRRE5fV0VCCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfU0VDUkVUS0VZCiAgICAgIC0gVEVNUE9SQUxfSE9TVD10ZW1wb3JhbAogICAgZGVwZW5kc19vbjoKICAgICAgLSBkYgogICAgICAtIHJlZGlzCiAgICAgIC0gY2xpY2tob3VzZQogICAgICAtIGthZmthCiAgICAgIC0gb2JqZWN0X3N0b3JhZ2UKICAgICAgLSB0ZW1wb3JhbAo=","tags":["analytics","product","open-source","self-hosted","ab-testing","event-tracking"],"logo":"svgs\/posthog.svg","minversion":"4.0.0-beta.222"},"reactive-resume":{"documentation":"https:\/\/rxresu.me\/?utm_source=coolify.io","slogan":"A one-of-a-kind resume builder that keeps your privacy in mind.","compose":"c2VydmljZXM6CiAgcmVhY3RpdmUtcmVzdW1lOgogICAgaW1hZ2U6ICdhbXJ1dGhwaWxsYWkvcmVhY3RpdmUtcmVzdW1lOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9SRUFDVElWRVJFU1VNRV8zMDAwCiAgICAgIC0gUFVCTElDX1VSTD0kU0VSVklDRV9GUUROX1JFQUNUSVZFUkVTVU1FCiAgICAgIC0gJ1NUT1JBR0VfVVJMPWh0dHA6Ly9taW5pbycKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzcWw6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU19EQjotcG9zdGdyZXN9JwogICAgICAtIEFDQ0VTU19UT0tFTl9TRUNSRVQ9JFNFUlZJQ0VfUEFTU1dPUkRfQUNDRVNTVE9LRU4KICAgICAgLSBSRUZSRVNIX1RPS0VOX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF9SRUZSRVNIVE9LRU4KICAgICAgLSBDSFJPTUVfVE9LRU49JFNFUlZJQ0VfUEFTU1dPUkRfQ0hST01FVE9LRU4KICAgICAgLSAnQ0hST01FX1VSTD13czovL2Nocm9tZTozMDAwJwogICAgICAtICdSRURJU19VUkw9cmVkaXM6Ly9yZWRpczo2Mzc5JwogICAgICAtIFNUT1JBR0VfRU5EUE9JTlQ9bWluaW8KICAgICAgLSBTVE9SQUdFX1BPUlQ9OTAwMAogICAgICAtIFNUT1JBR0VfUkVHSU9OPXVzLWVhc3QtMQogICAgICAtIFNUT1JBR0VfQlVDS0VUPWRlZmF1bHQKICAgICAgLSBTVE9SQUdFX0FDQ0VTU19LRVk9JFNFUlZJQ0VfVVNFUl9NSU5JTwogICAgICAtIFNUT1JBR0VfU0VDUkVUX0tFWT0kU0VSVklDRV9QQVNTV09SRF9NSU5JTwogICAgICAtIFNUT1JBR0VfVVNFX1NTTD1mYWxzZQogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3JlcwogICAgICAtIG1pbmlvCiAgICAgIC0gY2hyb21lCiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1wb3N0Z3Jlc30nCiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIG1pbmlvOgogICAgaW1hZ2U6ICdxdWF5LmlvL21pbmlvL21pbmlvOmxhdGVzdCcKICAgIGNvbW1hbmQ6ICdzZXJ2ZXIgL2RhdGEgLS1jb25zb2xlLWFkZHJlc3MgIjo5MDAxIicKICAgIGVudmlyb25tZW50OgogICAgICAtIE1JTklPX1JPT1RfVVNFUj0kU0VSVklDRV9VU0VSX01JTklPCiAgICAgIC0gTUlOSU9fUk9PVF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9NSU5JTwogICAgdm9sdW1lczoKICAgICAgLSAnbWluaW8tZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo5MDAwL21pbmlvL2hlYWx0aC9saXZlJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgY2hyb21lOgogICAgaW1hZ2U6ICdnaGNyLmlvL2Jyb3dzZXJsZXNzL2Nocm9tZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBIRUFMVEg9dHJ1ZQogICAgICAtIFRJTUVPVVQ9MTAwMDAKICAgICAgLSBDT05DVVJSRU5UPTEwCiAgICAgIC0gVE9LRU49JFNFUlZJQ0VfUEFTU1dPUkRfQ0hST01FVE9LRU4KICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6YWxwaW5lJwogICAgY29tbWFuZDogcmVkaXMtc2VydmVyCiAgICB2b2x1bWVzOgogICAgICAtICdyZWRpc19kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["reactive-resume","resume-builder","open-source","2fa"],"logo":"svgs\/rxresume.svg","minversion":"0.0.0","port":"3000"},"rocketchat":{"documentation":"https:\/\/github.com\/RocketChat\/Rocket.Chat?utm_source=coolify.io","slogan":"Self-hosted, secure and highly customizable open-source communication platform for organizations with sophisticated security and privacy concerns.","compose":"c2VydmljZXM6CiAgcm9ja2V0Y2hhdDoKICAgIGltYWdlOiAncmVnaXN0cnkucm9ja2V0LmNoYXQvcm9ja2V0Y2hhdC9yb2NrZXQuY2hhdDpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fUk9DS0VUQ0hBVF8zMDAwCiAgICAgIC0gJ01PTkdPX1VSTD1tb25nb2RiOi8vJHtNT05HT0RCX0FEVkVSVElTRURfSE9TVE5BTUU6LW1vbmdvZGJ9OiR7TU9OR09EQl9JTklUSUFMX1BSSU1BUllfUE9SVF9OVU1CRVI6LTI3MDE3fS8ke01PTkdPREJfREFUQUJBU0U6LXJvY2tldGNoYXR9P3JlcGxpY2FTZXQ9JHtNT05HT0RCX1JFUExJQ0FfU0VUX05BTUU6LXJzMH0nCiAgICAgIC0gJ01PTkdPX09QTE9HX1VSTD1tb25nb2RiOi8vJHtNT05HT0RCX0FEVkVSVElTRURfSE9TVE5BTUU6LW1vbmdvZGJ9OiR7TU9OR09EQl9JTklUSUFMX1BSSU1BUllfUE9SVF9OVU1CRVI6LTI3MDE3fS9sb2NhbD9yZXBsaWNhU2V0PSR7TU9OR09EQl9SRVBMSUNBX1NFVF9OQU1FOi1yczB9JwogICAgICAtIFJPT1RfVVJMPSRTRVJWSUNFX0ZRRE5fUk9DS0VUQ0hBVAogICAgICAtIERFUExPWV9NRVRIT0Q9ZG9ja2VyCiAgICAgIC0gUkVHX1RPS0VOPSRSRUdfVE9LRU4KICAgIGRlcGVuZHNfb246CiAgICAgIG1vbmdvZGI6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBub2RlCiAgICAgICAgLSAnLS1ldmFsJwogICAgICAgIC0gImNvbnN0IGh0dHAgPSByZXF1aXJlKCdodHRwJyk7IGNvbnN0IG9wdGlvbnMgPSB7IGhvc3Q6ICcwLjAuMC4wJywgcG9ydDogMzAwMCwgdGltZW91dDogMjAwMCwgcGF0aDogJy9oZWFsdGgnIH07IGNvbnN0IGhlYWx0aENoZWNrID0gaHR0cC5yZXF1ZXN0KG9wdGlvbnMsIChyZXMpID0+IHsgY29uc29sZS5sb2coJ0hFQUxUSENIRUNLIFNUQVRVUzonLCByZXMuc3RhdHVzQ29kZSk7IGlmIChyZXMuc3RhdHVzQ29kZSA9PSAyMDApIHsgcHJvY2Vzcy5leGl0KDApOyB9IGVsc2UgeyBwcm9jZXNzLmV4aXQoMSk7IH0gfSk7IGhlYWx0aENoZWNrLm9uKCdlcnJvcicsIGZ1bmN0aW9uIChlcnIpIHsgY29uc29sZS5lcnJvcignRVJST1InKTsgcHJvY2Vzcy5leGl0KDEpOyB9KTsgaGVhbHRoQ2hlY2suZW5kKCk7IgogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgbW9uZ29kYjoKICAgIGltYWdlOiAnZG9ja2VyLmlvL2JpdG5hbWkvbW9uZ29kYjo1LjAnCiAgICB2b2x1bWVzOgogICAgICAtICdtb25nb2RiX2RhdGE6L2JpdG5hbWkvbW9uZ29kYicKICAgIGVudmlyb25tZW50OgogICAgICAtIE1PTkdPREJfUkVQTElDQV9TRVRfTU9ERT1wcmltYXJ5CiAgICAgIC0gJ01PTkdPREJfUkVQTElDQV9TRVRfTkFNRT0ke01PTkdPREJfUkVQTElDQV9TRVRfTkFNRTotcnMwfScKICAgICAgLSAnTU9OR09EQl9QT1JUX05VTUJFUj0ke01PTkdPREJfUE9SVF9OVU1CRVI6LTI3MDE3fScKICAgICAgLSAnTU9OR09EQl9JTklUSUFMX1BSSU1BUllfSE9TVD0ke01PTkdPREJfSU5JVElBTF9QUklNQVJZX0hPU1Q6LW1vbmdvZGJ9JwogICAgICAtICdNT05HT0RCX0lOSVRJQUxfUFJJTUFSWV9QT1JUX05VTUJFUj0ke01PTkdPREJfSU5JVElBTF9QUklNQVJZX1BPUlRfTlVNQkVSOi0yNzAxN30nCiAgICAgIC0gJ01PTkdPREJfQURWRVJUSVNFRF9IT1NUTkFNRT0ke01PTkdPREJfQURWRVJUSVNFRF9IT1NUTkFNRTotbW9uZ29kYn0nCiAgICAgIC0gJ01PTkdPREJfRU5BQkxFX0pPVVJOQUw9JHtNT05HT0RCX0VOQUJMRV9KT1VSTkFMOi10cnVlfScKICAgICAgLSAnQUxMT1dfRU1QVFlfUEFTU1dPUkQ9JHtBTExPV19FTVBUWV9QQVNTV09SRDoteWVzfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAiZWNobyAnZGIuc3RhdHMoKS5vaycgfCBtb25nbyBsb2NhbGhvc3Q6MjcwMTcvdGVzdCAtLXF1aWV0IgogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==","tags":["rocketchat","chat","communication","privacy","mongodb","open","source"],"logo":"svgs\/rocketchat.svg","minversion":"0.0.0","port":"3000"},"shlink":{"documentation":"https:\/\/shlink.io\/?utm_source=coolify.io","slogan":"The definitive self-hosted URL shortener","compose":"c2VydmljZXM6CiAgc2hsaW5rOgogICAgaW1hZ2U6ICdzaGxpbmtpby9zaGxpbms6c3RhYmxlJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1NITElOS184MDgwCiAgICAgIC0gJ0RFRkFVTFRfRE9NQUlOPSR7U0VSVklDRV9VUkxfU0hMSU5LfScKICAgICAgLSBJU19IVFRQU19FTkFCTEVEPWZhbHNlCiAgICAgIC0gJ0lOSVRJQUxfQVBJX0tFWT0ke1NFUlZJQ0VfQkFTRTY0X1NITElOS0FQSUtFWX0nCiAgICB2b2x1bWVzOgogICAgICAtICdzaGxpbmstZGF0YTovZXRjL3NobGluay9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwODAvcmVzdC92My9oZWFsdGgnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICBzaGxpbmstd2ViOgogICAgaW1hZ2U6IHNobGlua2lvL3NobGluay13ZWItY2xpZW50CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fU0hMSU5LV0VCXzgwODAKICAgICAgLSAnU0hMSU5LX1NFUlZFUl9BUElfS0VZPSR7U0VSVklDRV9CQVNFNjRfU0hMSU5LQVBJS0VZfScKICAgICAgLSAnU0hMSU5LX1NFUlZFUl9VUkw9JHtTRVJWSUNFX0ZRRE5fU0hMSU5LfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MDgwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==","tags":["links","shortener","sharing","url","short","link","sharing"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"8080"},"slash":{"documentation":"https:\/\/github.com\/yourselfhosted\/slash?utm_source=coolify.io","slogan":"An open source, self-hosted links shortener and sharing platform.","compose":"c2VydmljZXM6CiAgc2xhc2g6CiAgICBpbWFnZTogeW91cnNlbGZob3N0ZWQvc2xhc2gKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9TTEFTSF81MjMxCiAgICB2b2x1bWVzOgogICAgICAtICdzbGFzaC1kYXRhOi92YXIvb3B0L3NsYXNoJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHdnZXQKICAgICAgICAtICctcScKICAgICAgICAtICctLXNwaWRlcicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjUyMzEnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["links","shortener","sharing","url","short","link","sharing"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"5231"},"snapdrop":{"documentation":"https:\/\/github.com\/RobinLinus\/snapdrop?utm_source=coolify.io","slogan":"A self-hosted file-sharing service for secure and convenient file transfers, whether on a local network or the internet.","compose":"c2VydmljZXM6CiAgc25hcGRyb3A6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvc25hcGRyb3A6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1NOQVBEUk9QCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnc25hcGRyb3AtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["file","sharing","transfer","local","network","internet"],"logo":"svgs\/unknown.svg","minversion":"0.0.0"},"statusnook":{"documentation":"https:\/\/statusnook.com?utm_source=coolify.io","slogan":"Effortlessly deploy a status page and start monitoring endpoints in minutes","compose":"c2VydmljZXM6CiAgc3RhdHVzbm9vazoKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9TVEFUVVNOT09LXzgwMDAKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3N0YXR1c25vb2stZGF0YTovYXBwL3N0YXR1c25vb2stZGF0YScKICAgIGltYWdlOiBnb2tzYW4vc3RhdHVzbm9vawogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHdnZXQKICAgICAgICAtICctcScKICAgICAgICAtICctLXNwaWRlcicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["go","html","monitoring","sqlite","self","hosted","status","page","htmx","smtp","slack"],"logo":"svgs\/statusnook.svg","minversion":"0.0.0","port":"8000"},"stirling-pdf":{"documentation":"https:\/\/github.com\/Stirling-Tools\/Stirling-PDF?utm_source=coolify.io","slogan":"Stirling is a powerful web based PDF manipulation tool","compose":"c2VydmljZXM6CiAgc3RpcmxpbmctcGRmOgogICAgaW1hZ2U6ICdmcm9vb2RsZS9zLXBkZjpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdzdGlybGluZy10cmFpbmluZy1kYXRhOi91c3Ivc2hhcmUvdGVzc2VyYWN0LW9jci81L3Rlc3NkYXRhJwogICAgICAtICdzdGlybGluZy1jb25maWdzOi9jb25maWdzJwogICAgICAtICdzdGlybGluZy1jdXN0b20tZmlsZXM6L2N1c3RvbUZpbGVzLycKICAgICAgLSAnc3RpcmxpbmctbG9nczovbG9ncy8nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fU1BERl84MDgwCiAgICAgIC0gRE9DS0VSX0VOQUJMRV9TRUNVUklUWT1mYWxzZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6ICdjdXJsIC0tZmFpbCAtSSBodHRwOi8vMTI3LjAuMC4xOjgwODAgfHwgZXhpdCAxJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["pdf","manipulation","web","tool"],"logo":"svgs\/stirling.png","minversion":"0.0.0","port":"8080"},"supabase":{"documentation":"https:\/\/supabase.io?utm_source=coolify.io","slogan":"The open source Firebase alternative.","compose":"c2VydmljZXM6CiAgc3VwYWJhc2Uta29uZzoKICAgIGltYWdlOiAna29uZzoyLjguMScKICAgIGVudHJ5cG9pbnQ6ICdiYXNoIC1jICcnZXZhbCAiZWNobyBcIiQkKGNhdCB+L3RlbXAueW1sKVwiIiA+IH4va29uZy55bWwgJiYgL2RvY2tlci1lbnRyeXBvaW50LnNoIGtvbmcgZG9ja2VyLXN0YXJ0JycnCiAgICBkZXBlbmRzX29uOgogICAgICBzdXBhYmFzZS1hbmFseXRpY3M6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9TVVBBQkFTRUtPTkcKICAgICAgLSAnSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfScKICAgICAgLSBLT05HX0RBVEFCQVNFPW9mZgogICAgICAtIEtPTkdfREVDTEFSQVRJVkVfQ09ORklHPS9ob21lL2tvbmcva29uZy55bWwKICAgICAgLSAnS09OR19ETlNfT1JERVI9TEFTVCxBLENOQU1FJwogICAgICAtICdLT05HX1BMVUdJTlM9cmVxdWVzdC10cmFuc2Zvcm1lcixjb3JzLGtleS1hdXRoLGFjbCxiYXNpYy1hdXRoJwogICAgICAtIEtPTkdfTkdJTlhfUFJPWFlfUFJPWFlfQlVGRkVSX1NJWkU9MTYwawogICAgICAtICdLT05HX05HSU5YX1BST1hZX1BST1hZX0JVRkZFUlM9NjQgMTYwaycKICAgICAgLSAnU1VQQUJBU0VfQU5PTl9LRVk9JHtTRVJWSUNFX1NVUEFCQVNFQU5PTl9LRVl9JwogICAgICAtICdTVVBBQkFTRV9TRVJWSUNFX0tFWT0ke1NFUlZJQ0VfU1VQQUJBU0VTRVJWSUNFX0tFWX0nCiAgICAgIC0gJ0RBU0hCT0FSRF9VU0VSTkFNRT0ke1NFUlZJQ0VfVVNFUl9BRE1JTn0nCiAgICAgIC0gJ0RBU0hCT0FSRF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfQURNSU59JwogICAgdm9sdW1lczoKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vdm9sdW1lcy9hcGkva29uZy55bWwKICAgICAgICB0YXJnZXQ6IC9ob21lL2tvbmcvdGVtcC55bWwKICAgICAgICBjb250ZW50OiAiX2Zvcm1hdF92ZXJzaW9uOiAnMi4xJ1xuX3RyYW5zZm9ybTogdHJ1ZVxuXG4jIyNcbiMjIyBDb25zdW1lcnMgLyBVc2Vyc1xuIyMjXG5jb25zdW1lcnM6XG4gIC0gdXNlcm5hbWU6IERBU0hCT0FSRFxuICAtIHVzZXJuYW1lOiBhbm9uXG4gICAga2V5YXV0aF9jcmVkZW50aWFsczpcbiAgICAgIC0ga2V5OiAkU1VQQUJBU0VfQU5PTl9LRVlcbiAgLSB1c2VybmFtZTogc2VydmljZV9yb2xlXG4gICAga2V5YXV0aF9jcmVkZW50aWFsczpcbiAgICAgIC0ga2V5OiAkU1VQQUJBU0VfU0VSVklDRV9LRVlcblxuIyMjXG4jIyMgQWNjZXNzIENvbnRyb2wgTGlzdFxuIyMjXG5hY2xzOlxuICAtIGNvbnN1bWVyOiBhbm9uXG4gICAgZ3JvdXA6IGFub25cbiAgLSBjb25zdW1lcjogc2VydmljZV9yb2xlXG4gICAgZ3JvdXA6IGFkbWluXG5cbiMjI1xuIyMjIERhc2hib2FyZCBjcmVkZW50aWFsc1xuIyMjXG5iYXNpY2F1dGhfY3JlZGVudGlhbHM6XG4tIGNvbnN1bWVyOiBEQVNIQk9BUkRcbiAgdXNlcm5hbWU6ICREQVNIQk9BUkRfVVNFUk5BTUVcbiAgcGFzc3dvcmQ6ICREQVNIQk9BUkRfUEFTU1dPUkRcblxuXG4jIyNcbiMjIyBBUEkgUm91dGVzXG4jIyNcbnNlcnZpY2VzOlxuXG4gICMjIE9wZW4gQXV0aCByb3V0ZXNcbiAgLSBuYW1lOiBhdXRoLXYxLW9wZW5cbiAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1hdXRoOjk5OTkvdmVyaWZ5XG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiBhdXRoLXYxLW9wZW5cbiAgICAgICAgc3RyaXBfcGF0aDogdHJ1ZVxuICAgICAgICBwYXRoczpcbiAgICAgICAgICAtIC9hdXRoL3YxL3ZlcmlmeVxuICAgIHBsdWdpbnM6XG4gICAgICAtIG5hbWU6IGNvcnNcbiAgLSBuYW1lOiBhdXRoLXYxLW9wZW4tY2FsbGJhY2tcbiAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1hdXRoOjk5OTkvY2FsbGJhY2tcbiAgICByb3V0ZXM6XG4gICAgICAtIG5hbWU6IGF1dGgtdjEtb3Blbi1jYWxsYmFja1xuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL2F1dGgvdjEvY2FsbGJhY2tcbiAgICBwbHVnaW5zOlxuICAgICAgLSBuYW1lOiBjb3JzXG4gIC0gbmFtZTogYXV0aC12MS1vcGVuLWF1dGhvcml6ZVxuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLWF1dGg6OTk5OS9hdXRob3JpemVcbiAgICByb3V0ZXM6XG4gICAgICAtIG5hbWU6IGF1dGgtdjEtb3Blbi1hdXRob3JpemVcbiAgICAgICAgc3RyaXBfcGF0aDogdHJ1ZVxuICAgICAgICBwYXRoczpcbiAgICAgICAgICAtIC9hdXRoL3YxL2F1dGhvcml6ZVxuICAgIHBsdWdpbnM6XG4gICAgICAtIG5hbWU6IGNvcnNcblxuICAjIyBTZWN1cmUgQXV0aCByb3V0ZXNcbiAgLSBuYW1lOiBhdXRoLXYxXG4gICAgX2NvbW1lbnQ6ICdHb1RydWU6IC9hdXRoL3YxLyogLT4gaHR0cDovL3N1cGFiYXNlLWF1dGg6OTk5OS8qJ1xuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLWF1dGg6OTk5OS9cbiAgICByb3V0ZXM6XG4gICAgICAtIG5hbWU6IGF1dGgtdjEtYWxsXG4gICAgICAgIHN0cmlwX3BhdGg6IHRydWVcbiAgICAgICAgcGF0aHM6XG4gICAgICAgICAgLSAvYXV0aC92MS9cbiAgICBwbHVnaW5zOlxuICAgICAgLSBuYW1lOiBjb3JzXG4gICAgICAtIG5hbWU6IGtleS1hdXRoXG4gICAgICAgIGNvbmZpZzpcbiAgICAgICAgICBoaWRlX2NyZWRlbnRpYWxzOiBmYWxzZVxuICAgICAgLSBuYW1lOiBhY2xcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGhpZGVfZ3JvdXBzX2hlYWRlcjogdHJ1ZVxuICAgICAgICAgIGFsbG93OlxuICAgICAgICAgICAgLSBhZG1pblxuICAgICAgICAgICAgLSBhbm9uXG5cbiAgIyMgU2VjdXJlIFJFU1Qgcm91dGVzXG4gIC0gbmFtZTogcmVzdC12MVxuICAgIF9jb21tZW50OiAnUG9zdGdSRVNUOiAvcmVzdC92MS8qIC0+IGh0dHA6Ly9zdXBhYmFzZS1yZXN0OjMwMDAvKidcbiAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1yZXN0OjMwMDAvXG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiByZXN0LXYxLWFsbFxuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL3Jlc3QvdjEvXG4gICAgcGx1Z2luczpcbiAgICAgIC0gbmFtZTogY29yc1xuICAgICAgLSBuYW1lOiBrZXktYXV0aFxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgaGlkZV9jcmVkZW50aWFsczogdHJ1ZVxuICAgICAgLSBuYW1lOiBhY2xcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGhpZGVfZ3JvdXBzX2hlYWRlcjogdHJ1ZVxuICAgICAgICAgIGFsbG93OlxuICAgICAgICAgICAgLSBhZG1pblxuICAgICAgICAgICAgLSBhbm9uXG5cbiAgIyMgU2VjdXJlIEdyYXBoUUwgcm91dGVzXG4gIC0gbmFtZTogZ3JhcGhxbC12MVxuICAgIF9jb21tZW50OiAnUG9zdGdSRVNUOiAvZ3JhcGhxbC92MS8qIC0+IGh0dHA6Ly9zdXBhYmFzZS1yZXN0OjMwMDAvcnBjL2dyYXBocWwnXG4gICAgdXJsOiBodHRwOi8vc3VwYWJhc2UtcmVzdDozMDAwL3JwYy9ncmFwaHFsXG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiBncmFwaHFsLXYxLWFsbFxuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL2dyYXBocWwvdjFcbiAgICBwbHVnaW5zOlxuICAgICAgLSBuYW1lOiBjb3JzXG4gICAgICAtIG5hbWU6IGtleS1hdXRoXG4gICAgICAgIGNvbmZpZzpcbiAgICAgICAgICBoaWRlX2NyZWRlbnRpYWxzOiB0cnVlXG4gICAgICAtIG5hbWU6IHJlcXVlc3QtdHJhbnNmb3JtZXJcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGFkZDpcbiAgICAgICAgICAgIGhlYWRlcnM6XG4gICAgICAgICAgICAgIC0gQ29udGVudC1Qcm9maWxlOmdyYXBocWxfcHVibGljXG4gICAgICAtIG5hbWU6IGFjbFxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgaGlkZV9ncm91cHNfaGVhZGVyOiB0cnVlXG4gICAgICAgICAgYWxsb3c6XG4gICAgICAgICAgICAtIGFkbWluXG4gICAgICAgICAgICAtIGFub25cblxuICAjIyBTZWN1cmUgUmVhbHRpbWUgcm91dGVzXG4gIC0gbmFtZTogcmVhbHRpbWUtdjEtd3NcbiAgICBfY29tbWVudDogJ1JlYWx0aW1lOiAvcmVhbHRpbWUvdjEvKiAtPiB3czovL3JlYWx0aW1lOjQwMDAvc29ja2V0LyonXG4gICAgdXJsOiBodHRwOi8vcmVhbHRpbWUtZGV2OjQwMDAvc29ja2V0XG4gICAgcHJvdG9jb2w6IHdzXG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiByZWFsdGltZS12MS13c1xuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL3JlYWx0aW1lL3YxL1xuICAgIHBsdWdpbnM6XG4gICAgICAtIG5hbWU6IGNvcnNcbiAgICAgIC0gbmFtZToga2V5LWF1dGhcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGhpZGVfY3JlZGVudGlhbHM6IGZhbHNlXG4gICAgICAtIG5hbWU6IGFjbFxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgaGlkZV9ncm91cHNfaGVhZGVyOiB0cnVlXG4gICAgICAgICAgYWxsb3c6XG4gICAgICAgICAgICAtIGFkbWluXG4gICAgICAgICAgICAtIGFub25cbiAgLSBuYW1lOiByZWFsdGltZS12MS1yZXN0XG4gICAgX2NvbW1lbnQ6ICdSZWFsdGltZTogL3JlYWx0aW1lL3YxLyogLT4gd3M6Ly9yZWFsdGltZTo0MDAwL3NvY2tldC8qJ1xuICAgIHVybDogaHR0cDovL3JlYWx0aW1lLWRldjo0MDAwL2FwaVxuICAgIHByb3RvY29sOiBodHRwXG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiByZWFsdGltZS12MS1yZXN0XG4gICAgICAgIHN0cmlwX3BhdGg6IHRydWVcbiAgICAgICAgcGF0aHM6XG4gICAgICAgICAgLSAvcmVhbHRpbWUvdjEvYXBpXG4gICAgcGx1Z2luczpcbiAgICAgIC0gbmFtZTogY29yc1xuICAgICAgLSBuYW1lOiBrZXktYXV0aFxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgaGlkZV9jcmVkZW50aWFsczogZmFsc2VcbiAgICAgIC0gbmFtZTogYWNsXG4gICAgICAgIGNvbmZpZzpcbiAgICAgICAgICBoaWRlX2dyb3Vwc19oZWFkZXI6IHRydWVcbiAgICAgICAgICBhbGxvdzpcbiAgICAgICAgICAgIC0gYWRtaW5cbiAgICAgICAgICAgIC0gYW5vblxuXG4gICMjIFN0b3JhZ2Ugcm91dGVzOiB0aGUgc3RvcmFnZSBzZXJ2ZXIgbWFuYWdlcyBpdHMgb3duIGF1dGhcbiAgLSBuYW1lOiBzdG9yYWdlLXYxXG4gICAgX2NvbW1lbnQ6ICdTdG9yYWdlOiAvc3RvcmFnZS92MS8qIC0+IGh0dHA6Ly9zdXBhYmFzZS1zdG9yYWdlOjUwMDAvKidcbiAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1zdG9yYWdlOjUwMDAvXG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiBzdG9yYWdlLXYxLWFsbFxuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL3N0b3JhZ2UvdjEvXG4gICAgcGx1Z2luczpcbiAgICAgIC0gbmFtZTogY29yc1xuXG4gICMjIEVkZ2UgRnVuY3Rpb25zIHJvdXRlc1xuICAtIG5hbWU6IGZ1bmN0aW9ucy12MVxuICAgIF9jb21tZW50OiAnRWRnZSBGdW5jdGlvbnM6IC9mdW5jdGlvbnMvdjEvKiAtPiBodHRwOi8vc3VwYWJhc2UtZWRnZS1mdW5jdGlvbnM6OTAwMC8qJ1xuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLWVkZ2UtZnVuY3Rpb25zOjkwMDAvXG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiBmdW5jdGlvbnMtdjEtYWxsXG4gICAgICAgIHN0cmlwX3BhdGg6IHRydWVcbiAgICAgICAgcGF0aHM6XG4gICAgICAgICAgLSAvZnVuY3Rpb25zL3YxL1xuICAgIHBsdWdpbnM6XG4gICAgICAtIG5hbWU6IGNvcnNcblxuICAjIyBBbmFseXRpY3Mgcm91dGVzXG4gIC0gbmFtZTogYW5hbHl0aWNzLXYxXG4gICAgX2NvbW1lbnQ6ICdBbmFseXRpY3M6IC9hbmFseXRpY3MvdjEvKiAtPiBodHRwOi8vbG9nZmxhcmU6NDAwMC8qJ1xuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLWFuYWx5dGljczo0MDAwL1xuICAgIHJvdXRlczpcbiAgICAgIC0gbmFtZTogYW5hbHl0aWNzLXYxLWFsbFxuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL2FuYWx5dGljcy92MS9cblxuICAjIyBTZWN1cmUgRGF0YWJhc2Ugcm91dGVzXG4gIC0gbmFtZTogbWV0YVxuICAgIF9jb21tZW50OiAncGctbWV0YTogL3BnLyogLT4gaHR0cDovL3N1cGFiYXNlLW1ldGE6ODA4MC8qJ1xuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLW1ldGE6ODA4MC9cbiAgICByb3V0ZXM6XG4gICAgICAtIG5hbWU6IG1ldGEtYWxsXG4gICAgICAgIHN0cmlwX3BhdGg6IHRydWVcbiAgICAgICAgcGF0aHM6XG4gICAgICAgICAgLSAvcGcvXG4gICAgcGx1Z2luczpcbiAgICAgIC0gbmFtZToga2V5LWF1dGhcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGhpZGVfY3JlZGVudGlhbHM6IGZhbHNlXG4gICAgICAtIG5hbWU6IGFjbFxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgaGlkZV9ncm91cHNfaGVhZGVyOiB0cnVlXG4gICAgICAgICAgYWxsb3c6XG4gICAgICAgICAgICAtIGFkbWluXG5cbiAgIyMgUHJvdGVjdGVkIERhc2hib2FyZCAtIGNhdGNoIGFsbCByZW1haW5pbmcgcm91dGVzXG4gIC0gbmFtZTogZGFzaGJvYXJkXG4gICAgX2NvbW1lbnQ6ICdTdHVkaW86IC8qIC0+IGh0dHA6Ly9zdHVkaW86MzAwMC8qJ1xuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLXN0dWRpbzozMDAwL1xuICAgIHJvdXRlczpcbiAgICAgIC0gbmFtZTogZGFzaGJvYXJkLWFsbFxuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL1xuICAgIHBsdWdpbnM6XG4gICAgICAtIG5hbWU6IGNvcnNcbiAgICAgIC0gbmFtZTogYmFzaWMtYXV0aFxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgaGlkZV9jcmVkZW50aWFsczogdHJ1ZVxuIgogIHN1cGFiYXNlLXN0dWRpbzoKICAgIGltYWdlOiAnc3VwYWJhc2Uvc3R1ZGlvOjIwMjQwNTE0LTZmNWNhYmQnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gbm9kZQogICAgICAgIC0gJy1lJwogICAgICAgIC0gInJlcXVpcmUoJ2h0dHAnKS5nZXQoJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMC9hcGkvcHJvZmlsZScsIChyKSA9PiB7aWYgKHIuc3RhdHVzQ29kZSAhPT0gMjAwKSBwcm9jZXNzLmV4aXQoMSk7IGVsc2UgcHJvY2Vzcy5leGl0KDApOyB9KS5vbignZXJyb3InLCAoKSA9PiBwcm9jZXNzLmV4aXQoMSkpIgogICAgICB0aW1lb3V0OiA1cwogICAgICBpbnRlcnZhbDogNXMKICAgICAgcmV0cmllczogMwogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtYW5hbHl0aWNzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBIT1NUTkFNRT0wLjAuMC4wCiAgICAgIC0gJ1NUVURJT19QR19NRVRBX1VSTD1odHRwOi8vc3VwYWJhc2UtbWV0YTo4MDgwJwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdERUZBVUxUX09SR0FOSVpBVElPTl9OQU1FPSR7U1RVRElPX0RFRkFVTFRfT1JHQU5JWkFUSU9OOi1EZWZhdWx0IE9yZ2FuaXphdGlvbn0nCiAgICAgIC0gJ0RFRkFVTFRfUFJPSkVDVF9OQU1FPSR7U1RVRElPX0RFRkFVTFRfUFJPSkVDVDotRGVmYXVsdCBQcm9qZWN0fScKICAgICAgLSAnU1VQQUJBU0VfVVJMPWh0dHA6Ly9zdXBhYmFzZS1rb25nOjgwMDAnCiAgICAgIC0gJ1NVUEFCQVNFX1BVQkxJQ19VUkw9JHtTRVJWSUNFX0ZRRE5fU1VQQUJBU0VLT05HfScKICAgICAgLSAnU1VQQUJBU0VfQU5PTl9LRVk9JHtTRVJWSUNFX1NVUEFCQVNFQU5PTl9LRVl9JwogICAgICAtICdTVVBBQkFTRV9TRVJWSUNFX0tFWT0ke1NFUlZJQ0VfU1VQQUJBU0VTRVJWSUNFX0tFWX0nCiAgICAgIC0gJ0FVVEhfSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfScKICAgICAgLSAnTE9HRkxBUkVfQVBJX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTE9HRkxBUkV9JwogICAgICAtICdMT0dGTEFSRV9VUkw9aHR0cDovL3N1cGFiYXNlLWFuYWx5dGljczo0MDAwJwogICAgICAtIE5FWFRfUFVCTElDX0VOQUJMRV9MT0dTPXRydWUKICAgICAgLSBORVhUX0FOQUxZVElDU19CQUNLRU5EX1BST1ZJREVSPXBvc3RncmVzCiAgc3VwYWJhc2UtZGI6CiAgICBpbWFnZTogJ3N1cGFiYXNlL3Bvc3RncmVzOjE1LjEuMS40MScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAncGdfaXNyZWFkeSAtVSBwb3N0Z3JlcyAtaCAxMjcuMC4wLjEnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiAxMAogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtdmVjdG9yOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBjb21tYW5kOgogICAgICAtIHBvc3RncmVzCiAgICAgIC0gJy1jJwogICAgICAtIGNvbmZpZ19maWxlPS9ldGMvcG9zdGdyZXNxbC9wb3N0Z3Jlc3FsLmNvbmYKICAgICAgLSAnLWMnCiAgICAgIC0gbG9nX21pbl9tZXNzYWdlcz1mYXRhbAogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX0hPU1Q9L3Zhci9ydW4vcG9zdGdyZXNxbAogICAgICAtICdQR1BPUlQ9JHtQT1NUR1JFU19QT1JUOi01NDMyfScKICAgICAgLSAnUE9TVEdSRVNfUE9SVD0ke1BPU1RHUkVTX1BPUlQ6LTU0MzJ9JwogICAgICAtICdQR1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gJ1BHREFUQUJBU0U9JHtQT1NUR1JFU19EQjotcG9zdGdyZXN9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1wb3N0Z3Jlc30nCiAgICAgIC0gJ0pXVF9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX0pXVH0nCiAgICAgIC0gJ0pXVF9FWFA9JHtKV1RfRVhQSVJZOi0zNjAwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3N1cGFiYXNlLWRiLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi92b2x1bWVzL2RiL3JlYWx0aW1lLnNxbAogICAgICAgIHRhcmdldDogL2RvY2tlci1lbnRyeXBvaW50LWluaXRkYi5kL21pZ3JhdGlvbnMvOTktcmVhbHRpbWUuc3FsCiAgICAgICAgY29udGVudDogIlxcc2V0IHBndXNlciBgZWNobyBcInN1cGFiYXNlX2FkbWluXCJgXG5cbmNyZWF0ZSBzY2hlbWEgaWYgbm90IGV4aXN0cyBfcmVhbHRpbWU7XG5hbHRlciBzY2hlbWEgX3JlYWx0aW1lIG93bmVyIHRvIDpwZ3VzZXI7XG4iCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL3ZvbHVtZXMvZGIvd2ViaG9va3Muc3FsCiAgICAgICAgdGFyZ2V0OiAvZG9ja2VyLWVudHJ5cG9pbnQtaW5pdGRiLmQvaW5pdC1zY3JpcHRzLzk4LXdlYmhvb2tzLnNxbAogICAgICAgIGNvbnRlbnQ6ICJCRUdJTjtcbi0tIENyZWF0ZSBwZ19uZXQgZXh0ZW5zaW9uXG5DUkVBVEUgRVhURU5TSU9OIElGIE5PVCBFWElTVFMgcGdfbmV0IFNDSEVNQSBleHRlbnNpb25zO1xuLS0gQ3JlYXRlIHN1cGFiYXNlX2Z1bmN0aW9ucyBzY2hlbWFcbkNSRUFURSBTQ0hFTUEgc3VwYWJhc2VfZnVuY3Rpb25zIEFVVEhPUklaQVRJT04gc3VwYWJhc2VfYWRtaW47XG5HUkFOVCBVU0FHRSBPTiBTQ0hFTUEgc3VwYWJhc2VfZnVuY3Rpb25zIFRPIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG5BTFRFUiBERUZBVUxUIFBSSVZJTEVHRVMgSU4gU0NIRU1BIHN1cGFiYXNlX2Z1bmN0aW9ucyBHUkFOVCBBTEwgT04gVEFCTEVTIFRPIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG5BTFRFUiBERUZBVUxUIFBSSVZJTEVHRVMgSU4gU0NIRU1BIHN1cGFiYXNlX2Z1bmN0aW9ucyBHUkFOVCBBTEwgT04gRlVOQ1RJT05TIFRPIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG5BTFRFUiBERUZBVUxUIFBSSVZJTEVHRVMgSU4gU0NIRU1BIHN1cGFiYXNlX2Z1bmN0aW9ucyBHUkFOVCBBTEwgT04gU0VRVUVOQ0VTIFRPIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG4tLSBzdXBhYmFzZV9mdW5jdGlvbnMubWlncmF0aW9ucyBkZWZpbml0aW9uXG5DUkVBVEUgVEFCTEUgc3VwYWJhc2VfZnVuY3Rpb25zLm1pZ3JhdGlvbnMgKFxuICB2ZXJzaW9uIHRleHQgUFJJTUFSWSBLRVksXG4gIGluc2VydGVkX2F0IHRpbWVzdGFtcHR6IE5PVCBOVUxMIERFRkFVTFQgTk9XKClcbik7XG4tLSBJbml0aWFsIHN1cGFiYXNlX2Z1bmN0aW9ucyBtaWdyYXRpb25cbklOU0VSVCBJTlRPIHN1cGFiYXNlX2Z1bmN0aW9ucy5taWdyYXRpb25zICh2ZXJzaW9uKSBWQUxVRVMgKCdpbml0aWFsJyk7XG4tLSBzdXBhYmFzZV9mdW5jdGlvbnMuaG9va3MgZGVmaW5pdGlvblxuQ1JFQVRFIFRBQkxFIHN1cGFiYXNlX2Z1bmN0aW9ucy5ob29rcyAoXG4gIGlkIGJpZ3NlcmlhbCBQUklNQVJZIEtFWSxcbiAgaG9va190YWJsZV9pZCBpbnRlZ2VyIE5PVCBOVUxMLFxuICBob29rX25hbWUgdGV4dCBOT1QgTlVMTCxcbiAgY3JlYXRlZF9hdCB0aW1lc3RhbXB0eiBOT1QgTlVMTCBERUZBVUxUIE5PVygpLFxuICByZXF1ZXN0X2lkIGJpZ2ludFxuKTtcbkNSRUFURSBJTkRFWCBzdXBhYmFzZV9mdW5jdGlvbnNfaG9va3NfcmVxdWVzdF9pZF9pZHggT04gc3VwYWJhc2VfZnVuY3Rpb25zLmhvb2tzIFVTSU5HIGJ0cmVlIChyZXF1ZXN0X2lkKTtcbkNSRUFURSBJTkRFWCBzdXBhYmFzZV9mdW5jdGlvbnNfaG9va3NfaF90YWJsZV9pZF9oX25hbWVfaWR4IE9OIHN1cGFiYXNlX2Z1bmN0aW9ucy5ob29rcyBVU0lORyBidHJlZSAoaG9va190YWJsZV9pZCwgaG9va19uYW1lKTtcbkNPTU1FTlQgT04gVEFCTEUgc3VwYWJhc2VfZnVuY3Rpb25zLmhvb2tzIElTICdTdXBhYmFzZSBGdW5jdGlvbnMgSG9va3M6IEF1ZGl0IHRyYWlsIGZvciB0cmlnZ2VyZWQgaG9va3MuJztcbkNSRUFURSBGVU5DVElPTiBzdXBhYmFzZV9mdW5jdGlvbnMuaHR0cF9yZXF1ZXN0KClcbiAgUkVUVVJOUyB0cmlnZ2VyXG4gIExBTkdVQUdFIHBscGdzcWxcbiAgQVMgJGZ1bmN0aW9uJFxuICBERUNMQVJFXG4gICAgcmVxdWVzdF9pZCBiaWdpbnQ7XG4gICAgcGF5bG9hZCBqc29uYjtcbiAgICB1cmwgdGV4dCA6PSBUR19BUkdWWzBdOjp0ZXh0O1xuICAgIG1ldGhvZCB0ZXh0IDo9IFRHX0FSR1ZbMV06OnRleHQ7XG4gICAgaGVhZGVycyBqc29uYiBERUZBVUxUICd7fSc6Ompzb25iO1xuICAgIHBhcmFtcyBqc29uYiBERUZBVUxUICd7fSc6Ompzb25iO1xuICAgIHRpbWVvdXRfbXMgaW50ZWdlciBERUZBVUxUIDEwMDA7XG4gIEJFR0lOXG4gICAgSUYgdXJsIElTIE5VTEwgT1IgdXJsID0gJ251bGwnIFRIRU5cbiAgICAgIFJBSVNFIEVYQ0VQVElPTiAndXJsIGFyZ3VtZW50IGlzIG1pc3NpbmcnO1xuICAgIEVORCBJRjtcblxuICAgIElGIG1ldGhvZCBJUyBOVUxMIE9SIG1ldGhvZCA9ICdudWxsJyBUSEVOXG4gICAgICBSQUlTRSBFWENFUFRJT04gJ21ldGhvZCBhcmd1bWVudCBpcyBtaXNzaW5nJztcbiAgICBFTkQgSUY7XG5cbiAgICBJRiBUR19BUkdWWzJdIElTIE5VTEwgT1IgVEdfQVJHVlsyXSA9ICdudWxsJyBUSEVOXG4gICAgICBoZWFkZXJzID0gJ3tcIkNvbnRlbnQtVHlwZVwiOiBcImFwcGxpY2F0aW9uL2pzb25cIn0nOjpqc29uYjtcbiAgICBFTFNFXG4gICAgICBoZWFkZXJzID0gVEdfQVJHVlsyXTo6anNvbmI7XG4gICAgRU5EIElGO1xuXG4gICAgSUYgVEdfQVJHVlszXSBJUyBOVUxMIE9SIFRHX0FSR1ZbM10gPSAnbnVsbCcgVEhFTlxuICAgICAgcGFyYW1zID0gJ3t9Jzo6anNvbmI7XG4gICAgRUxTRVxuICAgICAgcGFyYW1zID0gVEdfQVJHVlszXTo6anNvbmI7XG4gICAgRU5EIElGO1xuXG4gICAgSUYgVEdfQVJHVls0XSBJUyBOVUxMIE9SIFRHX0FSR1ZbNF0gPSAnbnVsbCcgVEhFTlxuICAgICAgdGltZW91dF9tcyA9IDEwMDA7XG4gICAgRUxTRVxuICAgICAgdGltZW91dF9tcyA9IFRHX0FSR1ZbNF06OmludGVnZXI7XG4gICAgRU5EIElGO1xuXG4gICAgQ0FTRVxuICAgICAgV0hFTiBtZXRob2QgPSAnR0VUJyBUSEVOXG4gICAgICAgIFNFTEVDVCBodHRwX2dldCBJTlRPIHJlcXVlc3RfaWQgRlJPTSBuZXQuaHR0cF9nZXQoXG4gICAgICAgICAgdXJsLFxuICAgICAgICAgIHBhcmFtcyxcbiAgICAgICAgICBoZWFkZXJzLFxuICAgICAgICAgIHRpbWVvdXRfbXNcbiAgICAgICAgKTtcbiAgICAgIFdIRU4gbWV0aG9kID0gJ1BPU1QnIFRIRU5cbiAgICAgICAgcGF5bG9hZCA9IGpzb25iX2J1aWxkX29iamVjdChcbiAgICAgICAgICAnb2xkX3JlY29yZCcsIE9MRCxcbiAgICAgICAgICAncmVjb3JkJywgTkVXLFxuICAgICAgICAgICd0eXBlJywgVEdfT1AsXG4gICAgICAgICAgJ3RhYmxlJywgVEdfVEFCTEVfTkFNRSxcbiAgICAgICAgICAnc2NoZW1hJywgVEdfVEFCTEVfU0NIRU1BXG4gICAgICAgICk7XG5cbiAgICAgICAgU0VMRUNUIGh0dHBfcG9zdCBJTlRPIHJlcXVlc3RfaWQgRlJPTSBuZXQuaHR0cF9wb3N0KFxuICAgICAgICAgIHVybCxcbiAgICAgICAgICBwYXlsb2FkLFxuICAgICAgICAgIHBhcmFtcyxcbiAgICAgICAgICBoZWFkZXJzLFxuICAgICAgICAgIHRpbWVvdXRfbXNcbiAgICAgICAgKTtcbiAgICAgIEVMU0VcbiAgICAgICAgUkFJU0UgRVhDRVBUSU9OICdtZXRob2QgYXJndW1lbnQgJSBpcyBpbnZhbGlkJywgbWV0aG9kO1xuICAgIEVORCBDQVNFO1xuXG4gICAgSU5TRVJUIElOVE8gc3VwYWJhc2VfZnVuY3Rpb25zLmhvb2tzXG4gICAgICAoaG9va190YWJsZV9pZCwgaG9va19uYW1lLCByZXF1ZXN0X2lkKVxuICAgIFZBTFVFU1xuICAgICAgKFRHX1JFTElELCBUR19OQU1FLCByZXF1ZXN0X2lkKTtcblxuICAgIFJFVFVSTiBORVc7XG4gIEVORFxuJGZ1bmN0aW9uJDtcbi0tIFN1cGFiYXNlIHN1cGVyIGFkbWluXG5ET1xuJCRcbkJFR0lOXG4gIElGIE5PVCBFWElTVFMgKFxuICAgIFNFTEVDVCAxXG4gICAgRlJPTSBwZ19yb2xlc1xuICAgIFdIRVJFIHJvbG5hbWUgPSAnc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluJ1xuICApXG4gIFRIRU5cbiAgICBDUkVBVEUgVVNFUiBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW4gTk9JTkhFUklUIENSRUFURVJPTEUgTE9HSU4gTk9SRVBMSUNBVElPTjtcbiAgRU5EIElGO1xuRU5EXG4kJDtcbkdSQU5UIEFMTCBQUklWSUxFR0VTIE9OIFNDSEVNQSBzdXBhYmFzZV9mdW5jdGlvbnMgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluO1xuR1JBTlQgQUxMIFBSSVZJTEVHRVMgT04gQUxMIFRBQkxFUyBJTiBTQ0hFTUEgc3VwYWJhc2VfZnVuY3Rpb25zIFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbjtcbkdSQU5UIEFMTCBQUklWSUxFR0VTIE9OIEFMTCBTRVFVRU5DRVMgSU4gU0NIRU1BIHN1cGFiYXNlX2Z1bmN0aW9ucyBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW47XG5BTFRFUiBVU0VSIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiBTRVQgc2VhcmNoX3BhdGggPSBcInN1cGFiYXNlX2Z1bmN0aW9uc1wiO1xuQUxURVIgdGFibGUgXCJzdXBhYmFzZV9mdW5jdGlvbnNcIi5taWdyYXRpb25zIE9XTkVSIFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbjtcbkFMVEVSIHRhYmxlIFwic3VwYWJhc2VfZnVuY3Rpb25zXCIuaG9va3MgT1dORVIgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluO1xuQUxURVIgZnVuY3Rpb24gXCJzdXBhYmFzZV9mdW5jdGlvbnNcIi5odHRwX3JlcXVlc3QoKSBPV05FUiBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW47XG5HUkFOVCBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW4gVE8gcG9zdGdyZXM7XG4tLSBSZW1vdmUgdW51c2VkIHN1cGFiYXNlX3BnX25ldF9hZG1pbiByb2xlXG5ET1xuJCRcbkJFR0lOXG4gIElGIEVYSVNUUyAoXG4gICAgU0VMRUNUIDFcbiAgICBGUk9NIHBnX3JvbGVzXG4gICAgV0hFUkUgcm9sbmFtZSA9ICdzdXBhYmFzZV9wZ19uZXRfYWRtaW4nXG4gIClcbiAgVEhFTlxuICAgIFJFQVNTSUdOIE9XTkVEIEJZIHN1cGFiYXNlX3BnX25ldF9hZG1pbiBUTyBzdXBhYmFzZV9hZG1pbjtcbiAgICBEUk9QIE9XTkVEIEJZIHN1cGFiYXNlX3BnX25ldF9hZG1pbjtcbiAgICBEUk9QIFJPTEUgc3VwYWJhc2VfcGdfbmV0X2FkbWluO1xuICBFTkQgSUY7XG5FTkRcbiQkO1xuLS0gcGdfbmV0IGdyYW50cyB3aGVuIGV4dGVuc2lvbiBpcyBhbHJlYWR5IGVuYWJsZWRcbkRPXG4kJFxuQkVHSU5cbiAgSUYgRVhJU1RTIChcbiAgICBTRUxFQ1QgMVxuICAgIEZST00gcGdfZXh0ZW5zaW9uXG4gICAgV0hFUkUgZXh0bmFtZSA9ICdwZ19uZXQnXG4gIClcbiAgVEhFTlxuICAgIEdSQU5UIFVTQUdFIE9OIFNDSEVNQSBuZXQgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluLCBwb3N0Z3JlcywgYW5vbiwgYXV0aGVudGljYXRlZCwgc2VydmljZV9yb2xlO1xuICAgIEFMVEVSIGZ1bmN0aW9uIG5ldC5odHRwX2dldCh1cmwgdGV4dCwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBTRUNVUklUWSBERUZJTkVSO1xuICAgIEFMVEVSIGZ1bmN0aW9uIG5ldC5odHRwX3Bvc3QodXJsIHRleHQsIGJvZHkganNvbmIsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgU0VDVVJJVFkgREVGSU5FUjtcbiAgICBBTFRFUiBmdW5jdGlvbiBuZXQuaHR0cF9nZXQodXJsIHRleHQsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgU0VUIHNlYXJjaF9wYXRoID0gbmV0O1xuICAgIEFMVEVSIGZ1bmN0aW9uIG5ldC5odHRwX3Bvc3QodXJsIHRleHQsIGJvZHkganNvbmIsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgU0VUIHNlYXJjaF9wYXRoID0gbmV0O1xuICAgIFJFVk9LRSBBTEwgT04gRlVOQ1RJT04gbmV0Lmh0dHBfZ2V0KHVybCB0ZXh0LCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIEZST00gUFVCTElDO1xuICAgIFJFVk9LRSBBTEwgT04gRlVOQ1RJT04gbmV0Lmh0dHBfcG9zdCh1cmwgdGV4dCwgYm9keSBqc29uYiwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBGUk9NIFBVQkxJQztcbiAgICBHUkFOVCBFWEVDVVRFIE9OIEZVTkNUSU9OIG5ldC5odHRwX2dldCh1cmwgdGV4dCwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW4sIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG4gICAgR1JBTlQgRVhFQ1VURSBPTiBGVU5DVElPTiBuZXQuaHR0cF9wb3N0KHVybCB0ZXh0LCBib2R5IGpzb25iLCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiwgcG9zdGdyZXMsIGFub24sIGF1dGhlbnRpY2F0ZWQsIHNlcnZpY2Vfcm9sZTtcbiAgRU5EIElGO1xuRU5EXG4kJDtcbi0tIEV2ZW50IHRyaWdnZXIgZm9yIHBnX25ldFxuQ1JFQVRFIE9SIFJFUExBQ0UgRlVOQ1RJT04gZXh0ZW5zaW9ucy5ncmFudF9wZ19uZXRfYWNjZXNzKClcblJFVFVSTlMgZXZlbnRfdHJpZ2dlclxuTEFOR1VBR0UgcGxwZ3NxbFxuQVMgJCRcbkJFR0lOXG4gIElGIEVYSVNUUyAoXG4gICAgU0VMRUNUIDFcbiAgICBGUk9NIHBnX2V2ZW50X3RyaWdnZXJfZGRsX2NvbW1hbmRzKCkgQVMgZXZcbiAgICBKT0lOIHBnX2V4dGVuc2lvbiBBUyBleHRcbiAgICBPTiBldi5vYmppZCA9IGV4dC5vaWRcbiAgICBXSEVSRSBleHQuZXh0bmFtZSA9ICdwZ19uZXQnXG4gIClcbiAgVEhFTlxuICAgIEdSQU5UIFVTQUdFIE9OIFNDSEVNQSBuZXQgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluLCBwb3N0Z3JlcywgYW5vbiwgYXV0aGVudGljYXRlZCwgc2VydmljZV9yb2xlO1xuICAgIEFMVEVSIGZ1bmN0aW9uIG5ldC5odHRwX2dldCh1cmwgdGV4dCwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBTRUNVUklUWSBERUZJTkVSO1xuICAgIEFMVEVSIGZ1bmN0aW9uIG5ldC5odHRwX3Bvc3QodXJsIHRleHQsIGJvZHkganNvbmIsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgU0VDVVJJVFkgREVGSU5FUjtcbiAgICBBTFRFUiBmdW5jdGlvbiBuZXQuaHR0cF9nZXQodXJsIHRleHQsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgU0VUIHNlYXJjaF9wYXRoID0gbmV0O1xuICAgIEFMVEVSIGZ1bmN0aW9uIG5ldC5odHRwX3Bvc3QodXJsIHRleHQsIGJvZHkganNvbmIsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgU0VUIHNlYXJjaF9wYXRoID0gbmV0O1xuICAgIFJFVk9LRSBBTEwgT04gRlVOQ1RJT04gbmV0Lmh0dHBfZ2V0KHVybCB0ZXh0LCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIEZST00gUFVCTElDO1xuICAgIFJFVk9LRSBBTEwgT04gRlVOQ1RJT04gbmV0Lmh0dHBfcG9zdCh1cmwgdGV4dCwgYm9keSBqc29uYiwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBGUk9NIFBVQkxJQztcbiAgICBHUkFOVCBFWEVDVVRFIE9OIEZVTkNUSU9OIG5ldC5odHRwX2dldCh1cmwgdGV4dCwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW4sIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG4gICAgR1JBTlQgRVhFQ1VURSBPTiBGVU5DVElPTiBuZXQuaHR0cF9wb3N0KHVybCB0ZXh0LCBib2R5IGpzb25iLCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiwgcG9zdGdyZXMsIGFub24sIGF1dGhlbnRpY2F0ZWQsIHNlcnZpY2Vfcm9sZTtcbiAgRU5EIElGO1xuRU5EO1xuJCQ7XG5DT01NRU5UIE9OIEZVTkNUSU9OIGV4dGVuc2lvbnMuZ3JhbnRfcGdfbmV0X2FjY2VzcyBJUyAnR3JhbnRzIGFjY2VzcyB0byBwZ19uZXQnO1xuRE9cbiQkXG5CRUdJTlxuICBJRiBOT1QgRVhJU1RTIChcbiAgICBTRUxFQ1QgMVxuICAgIEZST00gcGdfZXZlbnRfdHJpZ2dlclxuICAgIFdIRVJFIGV2dG5hbWUgPSAnaXNzdWVfcGdfbmV0X2FjY2VzcydcbiAgKSBUSEVOXG4gICAgQ1JFQVRFIEVWRU5UIFRSSUdHRVIgaXNzdWVfcGdfbmV0X2FjY2VzcyBPTiBkZGxfY29tbWFuZF9lbmQgV0hFTiBUQUcgSU4gKCdDUkVBVEUgRVhURU5TSU9OJylcbiAgICBFWEVDVVRFIFBST0NFRFVSRSBleHRlbnNpb25zLmdyYW50X3BnX25ldF9hY2Nlc3MoKTtcbiAgRU5EIElGO1xuRU5EXG4kJDtcbklOU0VSVCBJTlRPIHN1cGFiYXNlX2Z1bmN0aW9ucy5taWdyYXRpb25zICh2ZXJzaW9uKSBWQUxVRVMgKCcyMDIxMDgwOTE4MzQyM191cGRhdGVfZ3JhbnRzJyk7XG5BTFRFUiBmdW5jdGlvbiBzdXBhYmFzZV9mdW5jdGlvbnMuaHR0cF9yZXF1ZXN0KCkgU0VDVVJJVFkgREVGSU5FUjtcbkFMVEVSIGZ1bmN0aW9uIHN1cGFiYXNlX2Z1bmN0aW9ucy5odHRwX3JlcXVlc3QoKSBTRVQgc2VhcmNoX3BhdGggPSBzdXBhYmFzZV9mdW5jdGlvbnM7XG5SRVZPS0UgQUxMIE9OIEZVTkNUSU9OIHN1cGFiYXNlX2Z1bmN0aW9ucy5odHRwX3JlcXVlc3QoKSBGUk9NIFBVQkxJQztcbkdSQU5UIEVYRUNVVEUgT04gRlVOQ1RJT04gc3VwYWJhc2VfZnVuY3Rpb25zLmh0dHBfcmVxdWVzdCgpIFRPIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG5DT01NSVQ7XG4iCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL3ZvbHVtZXMvZGIvcm9sZXMuc3FsCiAgICAgICAgdGFyZ2V0OiAvZG9ja2VyLWVudHJ5cG9pbnQtaW5pdGRiLmQvaW5pdC1zY3JpcHRzLzk5LXJvbGVzLnNxbAogICAgICAgIGNvbnRlbnQ6ICItLSBOT1RFOiBjaGFuZ2UgdG8geW91ciBvd24gcGFzc3dvcmRzIGZvciBwcm9kdWN0aW9uIGVudmlyb25tZW50c1xuIFxcc2V0IHBncGFzcyBgZWNobyBcIiRQT1NUR1JFU19QQVNTV09SRFwiYFxuXG4gQUxURVIgVVNFUiBhdXRoZW50aWNhdG9yIFdJVEggUEFTU1dPUkQgOidwZ3Bhc3MnO1xuIEFMVEVSIFVTRVIgcGdib3VuY2VyIFdJVEggUEFTU1dPUkQgOidwZ3Bhc3MnO1xuIEFMVEVSIFVTRVIgc3VwYWJhc2VfYXV0aF9hZG1pbiBXSVRIIFBBU1NXT1JEIDoncGdwYXNzJztcbiBBTFRFUiBVU0VSIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiBXSVRIIFBBU1NXT1JEIDoncGdwYXNzJztcbiBBTFRFUiBVU0VSIHN1cGFiYXNlX3N0b3JhZ2VfYWRtaW4gV0lUSCBQQVNTV09SRCA6J3BncGFzcyc7XG4iCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL3ZvbHVtZXMvZGIvand0LnNxbAogICAgICAgIHRhcmdldDogL2RvY2tlci1lbnRyeXBvaW50LWluaXRkYi5kL2luaXQtc2NyaXB0cy85OS1qd3Quc3FsCiAgICAgICAgY29udGVudDogIlxcc2V0IGp3dF9zZWNyZXQgYGVjaG8gXCIkSldUX1NFQ1JFVFwiYFxuXFxzZXQgand0X2V4cCBgZWNobyBcIiRKV1RfRVhQXCJgXG5cXHNldCBkYl9uYW1lIGBlY2hvIFwiJHtQT1NUR1JFU19EQjotcG9zdGdyZXN9XCJgXG5cbkFMVEVSIERBVEFCQVNFIDpkYl9uYW1lIFNFVCBcImFwcC5zZXR0aW5ncy5qd3Rfc2VjcmV0XCIgVE8gOidqd3Rfc2VjcmV0JztcbkFMVEVSIERBVEFCQVNFIDpkYl9uYW1lIFNFVCBcImFwcC5zZXR0aW5ncy5qd3RfZXhwXCIgVE8gOidqd3RfZXhwJztcbiIKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vdm9sdW1lcy9kYi9sb2dzLnNxbAogICAgICAgIHRhcmdldDogL2RvY2tlci1lbnRyeXBvaW50LWluaXRkYi5kL21pZ3JhdGlvbnMvOTktbG9ncy5zcWwKICAgICAgICBjb250ZW50OiAiXFxzZXQgcGd1c2VyIGBlY2hvIFwic3VwYWJhc2VfYWRtaW5cImBcblxuY3JlYXRlIHNjaGVtYSBpZiBub3QgZXhpc3RzIF9hbmFseXRpY3M7XG5hbHRlciBzY2hlbWEgX2FuYWx5dGljcyBvd25lciB0byA6cGd1c2VyO1xuIgogICAgICAtICdzdXBhYmFzZS1kYi1jb25maWc6L2V0Yy9wb3N0Z3Jlc3FsLWN1c3RvbScKICBzdXBhYmFzZS1hbmFseXRpY3M6CiAgICBpbWFnZTogJ3N1cGFiYXNlL2xvZ2ZsYXJlOjEuNC4wJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjQwMDAvaGVhbHRoJwogICAgICB0aW1lb3V0OiA1cwogICAgICBpbnRlcnZhbDogNXMKICAgICAgcmV0cmllczogMTAKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBkZXBlbmRzX29uOgogICAgICBzdXBhYmFzZS1kYjoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTE9HRkxBUkVfTk9ERV9IT1NUPTEyNy4wLjAuMQogICAgICAtIERCX1VTRVJOQU1FPXN1cGFiYXNlX2FkbWluCiAgICAgIC0gJ0RCX0RBVEFCQVNFPSR7UE9TVEdSRVNfREI6LXBvc3RncmVzfScKICAgICAgLSAnREJfSE9TVE5BTUU9JHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn0nCiAgICAgIC0gJ0RCX1BPUlQ9JHtQT1NUR1JFU19QT1JUOi01NDMyfScKICAgICAgLSAnREJfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfScKICAgICAgLSBEQl9TQ0hFTUE9X2FuYWx5dGljcwogICAgICAtICdMT0dGTEFSRV9BUElfS0VZPSR7U0VSVklDRV9QQVNTV09SRF9MT0dGTEFSRX0nCiAgICAgIC0gTE9HRkxBUkVfU0lOR0xFX1RFTkFOVD10cnVlCiAgICAgIC0gTE9HRkxBUkVfU0lOR0xFX1RFTkFOVF9NT0RFPXRydWUKICAgICAgLSBMT0dGTEFSRV9TVVBBQkFTRV9NT0RFPXRydWUKICAgICAgLSBMT0dGTEFSRV9NSU5fQ0xVU1RFUl9TSVpFPTEKICAgICAgLSAnUE9TVEdSRVNfQkFDS0VORF9VUkw9cG9zdGdyZXNxbDovL3N1cGFiYXNlX2FkbWluOiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AJHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn06JHtQT1NUR1JFU19QT1JUOi01NDMyfS8ke1BPU1RHUkVTX0RCOi1wb3N0Z3Jlc30nCiAgICAgIC0gUE9TVEdSRVNfQkFDS0VORF9TQ0hFTUE9X2FuYWx5dGljcwogICAgICAtIExPR0ZMQVJFX0ZFQVRVUkVfRkxBR19PVkVSUklERT1tdWx0aWJhY2tlbmQ9dHJ1ZQogIHN1cGFiYXNlLXZlY3RvcjoKICAgIGltYWdlOiAndGltYmVyaW8vdmVjdG9yOjAuMjguMS1hbHBpbmUnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gd2dldAogICAgICAgIC0gJy0tbm8tdmVyYm9zZScKICAgICAgICAtICctLXRyaWVzPTEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovL3N1cGFiYXNlLXZlY3Rvcjo5MDAxL2hlYWx0aCcKICAgICAgdGltZW91dDogNXMKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHJldHJpZXM6IDMKICAgIHZvbHVtZXM6CiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL3ZvbHVtZXMvbG9ncy92ZWN0b3IueW1sCiAgICAgICAgdGFyZ2V0OiAvZXRjL3ZlY3Rvci92ZWN0b3IueW1sCiAgICAgICAgcmVhZF9vbmx5OiB0cnVlCiAgICAgICAgY29udGVudDogImFwaTpcbiAgZW5hYmxlZDogdHJ1ZVxuICBhZGRyZXNzOiAwLjAuMC4wOjkwMDFcblxuc291cmNlczpcbiAgZG9ja2VyX2hvc3Q6XG4gICAgdHlwZTogZG9ja2VyX2xvZ3NcbiAgICBleGNsdWRlX2NvbnRhaW5lcnM6XG4gICAgICAtIHN1cGFiYXNlLXZlY3RvclxuXG50cmFuc2Zvcm1zOlxuICBwcm9qZWN0X2xvZ3M6XG4gICAgdHlwZTogcmVtYXBcbiAgICBpbnB1dHM6XG4gICAgICAtIGRvY2tlcl9ob3N0XG4gICAgc291cmNlOiB8LVxuICAgICAgLnByb2plY3QgPSBcImRlZmF1bHRcIlxuICAgICAgLmV2ZW50X21lc3NhZ2UgPSBkZWwoLm1lc3NhZ2UpXG4gICAgICAuYXBwbmFtZSA9IGRlbCguY29udGFpbmVyX25hbWUpXG4gICAgICBkZWwoLmNvbnRhaW5lcl9jcmVhdGVkX2F0KVxuICAgICAgZGVsKC5jb250YWluZXJfaWQpXG4gICAgICBkZWwoLnNvdXJjZV90eXBlKVxuICAgICAgZGVsKC5zdHJlYW0pXG4gICAgICBkZWwoLmxhYmVsKVxuICAgICAgZGVsKC5pbWFnZSlcbiAgICAgIGRlbCguaG9zdClcbiAgICAgIGRlbCguc3RyZWFtKVxuICByb3V0ZXI6XG4gICAgdHlwZTogcm91dGVcbiAgICBpbnB1dHM6XG4gICAgICAtIHByb2plY3RfbG9nc1xuICAgIHJvdXRlOlxuICAgICAga29uZzogJ3N0YXJ0c193aXRoKHN0cmluZyEoLmFwcG5hbWUpLCBcInN1cGFiYXNlLWtvbmdcIiknXG4gICAgICBhdXRoOiAnc3RhcnRzX3dpdGgoc3RyaW5nISguYXBwbmFtZSksIFwic3VwYWJhc2UtYXV0aFwiKSdcbiAgICAgIHJlc3Q6ICdzdGFydHNfd2l0aChzdHJpbmchKC5hcHBuYW1lKSwgXCJzdXBhYmFzZS1yZXN0XCIpJ1xuICAgICAgcmVhbHRpbWU6ICdzdGFydHNfd2l0aChzdHJpbmchKC5hcHBuYW1lKSwgXCJyZWFsdGltZS1kZXZcIiknXG4gICAgICBzdG9yYWdlOiAnc3RhcnRzX3dpdGgoc3RyaW5nISguYXBwbmFtZSksIFwic3VwYWJhc2Utc3RvcmFnZVwiKSdcbiAgICAgIGZ1bmN0aW9uczogJ3N0YXJ0c193aXRoKHN0cmluZyEoLmFwcG5hbWUpLCBcInN1cGFiYXNlLWZ1bmN0aW9uc1wiKSdcbiAgICAgIGRiOiAnc3RhcnRzX3dpdGgoc3RyaW5nISguYXBwbmFtZSksIFwic3VwYWJhc2UtZGJcIiknXG4gICMgSWdub3JlcyBub24gbmdpbnggZXJyb3JzIHNpbmNlIHRoZXkgYXJlIHJlbGF0ZWQgd2l0aCBrb25nIGJvb3RpbmcgdXBcbiAga29uZ19sb2dzOlxuICAgIHR5cGU6IHJlbWFwXG4gICAgaW5wdXRzOlxuICAgICAgLSByb3V0ZXIua29uZ1xuICAgIHNvdXJjZTogfC1cbiAgICAgIHJlcSwgZXJyID0gcGFyc2VfbmdpbnhfbG9nKC5ldmVudF9tZXNzYWdlLCBcImNvbWJpbmVkXCIpXG4gICAgICBpZiBlcnIgPT0gbnVsbCB7XG4gICAgICAgICAgLnRpbWVzdGFtcCA9IHJlcS50aW1lc3RhbXBcbiAgICAgICAgICAubWV0YWRhdGEucmVxdWVzdC5oZWFkZXJzLnJlZmVyZXIgPSByZXEucmVmZXJlclxuICAgICAgICAgIC5tZXRhZGF0YS5yZXF1ZXN0LmhlYWRlcnMudXNlcl9hZ2VudCA9IHJlcS5hZ2VudFxuICAgICAgICAgIC5tZXRhZGF0YS5yZXF1ZXN0LmhlYWRlcnMuY2ZfY29ubmVjdGluZ19pcCA9IHJlcS5jbGllbnRcbiAgICAgICAgICAubWV0YWRhdGEucmVxdWVzdC5tZXRob2QgPSByZXEubWV0aG9kXG4gICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QucGF0aCA9IHJlcS5wYXRoXG4gICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QucHJvdG9jb2wgPSByZXEucHJvdG9jb2xcbiAgICAgICAgICAubWV0YWRhdGEucmVzcG9uc2Uuc3RhdHVzX2NvZGUgPSByZXEuc3RhdHVzXG4gICAgICB9XG4gICAgICBpZiBlcnIgIT0gbnVsbCB7XG4gICAgICAgIGFib3J0XG4gICAgICB9XG4gICMgSWdub3JlcyBub24gbmdpbnggZXJyb3JzIHNpbmNlIHRoZXkgYXJlIHJlbGF0ZWQgd2l0aCBrb25nIGJvb3RpbmcgdXBcbiAga29uZ19lcnI6XG4gICAgdHlwZTogcmVtYXBcbiAgICBpbnB1dHM6XG4gICAgICAtIHJvdXRlci5rb25nXG4gICAgc291cmNlOiB8LVxuICAgICAgLm1ldGFkYXRhLnJlcXVlc3QubWV0aG9kID0gXCJHRVRcIlxuICAgICAgLm1ldGFkYXRhLnJlc3BvbnNlLnN0YXR1c19jb2RlID0gMjAwXG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX25naW54X2xvZyguZXZlbnRfbWVzc2FnZSwgXCJlcnJvclwiKVxuICAgICAgaWYgZXJyID09IG51bGwge1xuICAgICAgICAgIC50aW1lc3RhbXAgPSBwYXJzZWQudGltZXN0YW1wXG4gICAgICAgICAgLnNldmVyaXR5ID0gcGFyc2VkLnNldmVyaXR5XG4gICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QuaG9zdCA9IHBhcnNlZC5ob3N0XG4gICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QuaGVhZGVycy5jZl9jb25uZWN0aW5nX2lwID0gcGFyc2VkLmNsaWVudFxuICAgICAgICAgIHVybCwgZXJyID0gc3BsaXQocGFyc2VkLnJlcXVlc3QsIFwiIFwiKVxuICAgICAgICAgIGlmIGVyciA9PSBudWxsIHtcbiAgICAgICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QubWV0aG9kID0gdXJsWzBdXG4gICAgICAgICAgICAgIC5tZXRhZGF0YS5yZXF1ZXN0LnBhdGggPSB1cmxbMV1cbiAgICAgICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QucHJvdG9jb2wgPSB1cmxbMl1cbiAgICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiBlcnIgIT0gbnVsbCB7XG4gICAgICAgIGFib3J0XG4gICAgICB9XG4gICMgR290cnVlIGxvZ3MgYXJlIHN0cnVjdHVyZWQganNvbiBzdHJpbmdzIHdoaWNoIGZyb250ZW5kIHBhcnNlcyBkaXJlY3RseS4gQnV0IHdlIGtlZXAgbWV0YWRhdGEgZm9yIGNvbnNpc3RlbmN5LlxuICBhdXRoX2xvZ3M6XG4gICAgdHlwZTogcmVtYXBcbiAgICBpbnB1dHM6XG4gICAgICAtIHJvdXRlci5hdXRoXG4gICAgc291cmNlOiB8LVxuICAgICAgcGFyc2VkLCBlcnIgPSBwYXJzZV9qc29uKC5ldmVudF9tZXNzYWdlKVxuICAgICAgaWYgZXJyID09IG51bGwge1xuICAgICAgICAgIC5tZXRhZGF0YS50aW1lc3RhbXAgPSBwYXJzZWQudGltZVxuICAgICAgICAgIC5tZXRhZGF0YSA9IG1lcmdlISgubWV0YWRhdGEsIHBhcnNlZClcbiAgICAgIH1cbiAgIyBQb3N0Z1JFU1QgbG9ncyBhcmUgc3RydWN0dXJlZCBzbyB3ZSBzZXBhcmF0ZSB0aW1lc3RhbXAgZnJvbSBtZXNzYWdlIHVzaW5nIHJlZ2V4XG4gIHJlc3RfbG9nczpcbiAgICB0eXBlOiByZW1hcFxuICAgIGlucHV0czpcbiAgICAgIC0gcm91dGVyLnJlc3RcbiAgICBzb3VyY2U6IHwtXG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX3JlZ2V4KC5ldmVudF9tZXNzYWdlLCByJ14oP1A8dGltZT4uKik6ICg\/UDxtc2c+LiopJCcpXG4gICAgICBpZiBlcnIgPT0gbnVsbCB7XG4gICAgICAgICAgLmV2ZW50X21lc3NhZ2UgPSBwYXJzZWQubXNnXG4gICAgICAgICAgLnRpbWVzdGFtcCA9IHRvX3RpbWVzdGFtcCEocGFyc2VkLnRpbWUpXG4gICAgICAgICAgLm1ldGFkYXRhLmhvc3QgPSAucHJvamVjdFxuICAgICAgfVxuICAjIFJlYWx0aW1lIGxvZ3MgYXJlIHN0cnVjdHVyZWQgc28gd2UgcGFyc2UgdGhlIHNldmVyaXR5IGxldmVsIHVzaW5nIHJlZ2V4IChpZ25vcmUgdGltZSBiZWNhdXNlIGl0IGhhcyBubyBkYXRlKVxuICByZWFsdGltZV9sb2dzOlxuICAgIHR5cGU6IHJlbWFwXG4gICAgaW5wdXRzOlxuICAgICAgLSByb3V0ZXIucmVhbHRpbWVcbiAgICBzb3VyY2U6IHwtXG4gICAgICAubWV0YWRhdGEucHJvamVjdCA9IGRlbCgucHJvamVjdClcbiAgICAgIC5tZXRhZGF0YS5leHRlcm5hbF9pZCA9IC5tZXRhZGF0YS5wcm9qZWN0XG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX3JlZ2V4KC5ldmVudF9tZXNzYWdlLCByJ14oP1A8dGltZT5cXGQrOlxcZCs6XFxkK1xcLlxcZCspIFxcWyg\/UDxsZXZlbD5cXHcrKVxcXSAoP1A8bXNnPi4qKSQnKVxuICAgICAgaWYgZXJyID09IG51bGwge1xuICAgICAgICAgIC5ldmVudF9tZXNzYWdlID0gcGFyc2VkLm1zZ1xuICAgICAgICAgIC5tZXRhZGF0YS5sZXZlbCA9IHBhcnNlZC5sZXZlbFxuICAgICAgfVxuICAjIFN0b3JhZ2UgbG9ncyBtYXkgY29udGFpbiBqc29uIG9iamVjdHMgc28gd2UgcGFyc2UgdGhlbSBmb3IgY29tcGxldGVuZXNzXG4gIHN0b3JhZ2VfbG9nczpcbiAgICB0eXBlOiByZW1hcFxuICAgIGlucHV0czpcbiAgICAgIC0gcm91dGVyLnN0b3JhZ2VcbiAgICBzb3VyY2U6IHwtXG4gICAgICAubWV0YWRhdGEucHJvamVjdCA9IGRlbCgucHJvamVjdClcbiAgICAgIC5tZXRhZGF0YS50ZW5hbnRJZCA9IC5tZXRhZGF0YS5wcm9qZWN0XG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX2pzb24oLmV2ZW50X21lc3NhZ2UpXG4gICAgICBpZiBlcnIgPT0gbnVsbCB7XG4gICAgICAgICAgLmV2ZW50X21lc3NhZ2UgPSBwYXJzZWQubXNnXG4gICAgICAgICAgLm1ldGFkYXRhLmxldmVsID0gcGFyc2VkLmxldmVsXG4gICAgICAgICAgLm1ldGFkYXRhLnRpbWVzdGFtcCA9IHBhcnNlZC50aW1lXG4gICAgICAgICAgLm1ldGFkYXRhLmNvbnRleHRbMF0uaG9zdCA9IHBhcnNlZC5ob3N0bmFtZVxuICAgICAgICAgIC5tZXRhZGF0YS5jb250ZXh0WzBdLnBpZCA9IHBhcnNlZC5waWRcbiAgICAgIH1cbiAgIyBQb3N0Z3JlcyBsb2dzIHNvbWUgbWVzc2FnZXMgdG8gc3RkZXJyIHdoaWNoIHdlIG1hcCB0byB3YXJuaW5nIHNldmVyaXR5IGxldmVsXG4gIGRiX2xvZ3M6XG4gICAgdHlwZTogcmVtYXBcbiAgICBpbnB1dHM6XG4gICAgICAtIHJvdXRlci5kYlxuICAgIHNvdXJjZTogfC1cbiAgICAgIC5tZXRhZGF0YS5ob3N0ID0gXCJkYi1kZWZhdWx0XCJcbiAgICAgIC5tZXRhZGF0YS5wYXJzZWQudGltZXN0YW1wID0gLnRpbWVzdGFtcFxuXG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX3JlZ2V4KC5ldmVudF9tZXNzYWdlLCByJy4qKD9QPGxldmVsPklORk98Tk9USUNFfFdBUk5JTkd8RVJST1J8TE9HfEZBVEFMfFBBTklDPyk6LionLCBudW1lcmljX2dyb3VwczogdHJ1ZSlcblxuICAgICAgaWYgZXJyICE9IG51bGwgfHwgcGFyc2VkID09IG51bGwge1xuICAgICAgICAubWV0YWRhdGEucGFyc2VkLmVycm9yX3NldmVyaXR5ID0gXCJpbmZvXCJcbiAgICAgIH1cbiAgICAgIGlmIHBhcnNlZCAhPSBudWxsIHtcbiAgICAgIC5tZXRhZGF0YS5wYXJzZWQuZXJyb3Jfc2V2ZXJpdHkgPSBwYXJzZWQubGV2ZWxcbiAgICAgIH1cbiAgICAgIGlmIC5tZXRhZGF0YS5wYXJzZWQuZXJyb3Jfc2V2ZXJpdHkgPT0gXCJpbmZvXCIge1xuICAgICAgICAgIC5tZXRhZGF0YS5wYXJzZWQuZXJyb3Jfc2V2ZXJpdHkgPSBcImxvZ1wiXG4gICAgICB9XG4gICAgICAubWV0YWRhdGEucGFyc2VkLmVycm9yX3NldmVyaXR5ID0gdXBjYXNlISgubWV0YWRhdGEucGFyc2VkLmVycm9yX3NldmVyaXR5KVxuXG5zaW5rczpcbiAgbG9nZmxhcmVfYXV0aDpcbiAgICB0eXBlOiAnaHR0cCdcbiAgICBpbnB1dHM6XG4gICAgICAtIGF1dGhfbG9nc1xuICAgIGVuY29kaW5nOlxuICAgICAgY29kZWM6ICdqc29uJ1xuICAgIG1ldGhvZDogJ3Bvc3QnXG4gICAgcmVxdWVzdDpcbiAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMFxuICAgIHVyaTogJ2h0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMC9hcGkvbG9ncz9zb3VyY2VfbmFtZT1nb3RydWUubG9ncy5wcm9kJmFwaV9rZXk9JHtMT0dGTEFSRV9BUElfS0VZP0xPR0ZMQVJFX0FQSV9LRVkgaXMgcmVxdWlyZWR9J1xuICBsb2dmbGFyZV9yZWFsdGltZTpcbiAgICB0eXBlOiAnaHR0cCdcbiAgICBpbnB1dHM6XG4gICAgICAtIHJlYWx0aW1lX2xvZ3NcbiAgICBlbmNvZGluZzpcbiAgICAgIGNvZGVjOiAnanNvbidcbiAgICBtZXRob2Q6ICdwb3N0J1xuICAgIHJlcXVlc3Q6XG4gICAgICByZXRyeV9tYXhfZHVyYXRpb25fc2VjczogMTBcbiAgICB1cmk6ICdodHRwOi8vc3VwYWJhc2UtYW5hbHl0aWNzOjQwMDAvYXBpL2xvZ3M\/c291cmNlX25hbWU9cmVhbHRpbWUubG9ncy5wcm9kJmFwaV9rZXk9JHtMT0dGTEFSRV9BUElfS0VZP0xPR0ZMQVJFX0FQSV9LRVkgaXMgcmVxdWlyZWR9J1xuICBsb2dmbGFyZV9yZXN0OlxuICAgIHR5cGU6ICdodHRwJ1xuICAgIGlucHV0czpcbiAgICAgIC0gcmVzdF9sb2dzXG4gICAgZW5jb2Rpbmc6XG4gICAgICBjb2RlYzogJ2pzb24nXG4gICAgbWV0aG9kOiAncG9zdCdcbiAgICByZXF1ZXN0OlxuICAgICAgcmV0cnlfbWF4X2R1cmF0aW9uX3NlY3M6IDEwXG4gICAgdXJpOiAnaHR0cDovL3N1cGFiYXNlLWFuYWx5dGljczo0MDAwL2FwaS9sb2dzP3NvdXJjZV9uYW1lPXBvc3RnUkVTVC5sb2dzLnByb2QmYXBpX2tleT0ke0xPR0ZMQVJFX0FQSV9LRVk\/TE9HRkxBUkVfQVBJX0tFWSBpcyByZXF1aXJlZH0nXG4gIGxvZ2ZsYXJlX2RiOlxuICAgIHR5cGU6ICdodHRwJ1xuICAgIGlucHV0czpcbiAgICAgIC0gZGJfbG9nc1xuICAgIGVuY29kaW5nOlxuICAgICAgY29kZWM6ICdqc29uJ1xuICAgIG1ldGhvZDogJ3Bvc3QnXG4gICAgcmVxdWVzdDpcbiAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMFxuICAgICMgV2UgbXVzdCByb3V0ZSB0aGUgc2luayB0aHJvdWdoIGtvbmcgYmVjYXVzZSBpbmdlc3RpbmcgbG9ncyBiZWZvcmUgbG9nZmxhcmUgaXMgZnVsbHkgaW5pdGlhbGlzZWQgd2lsbFxuICAgICMgbGVhZCB0byBicm9rZW4gcXVlcmllcyBmcm9tIHN0dWRpby4gVGhpcyB3b3JrcyBieSB0aGUgYXNzdW1wdGlvbiB0aGF0IGNvbnRhaW5lcnMgYXJlIHN0YXJ0ZWQgaW4gdGhlXG4gICAgIyBmb2xsb3dpbmcgb3JkZXI6IHZlY3RvciA+IGRiID4gbG9nZmxhcmUgPiBrb25nXG4gICAgdXJpOiAnaHR0cDovL3N1cGFiYXNlLWtvbmc6ODAwMC9hbmFseXRpY3MvdjEvYXBpL2xvZ3M\/c291cmNlX25hbWU9cG9zdGdyZXMubG9ncyZhcGlfa2V5PSR7TE9HRkxBUkVfQVBJX0tFWT9MT0dGTEFSRV9BUElfS0VZIGlzIHJlcXVpcmVkfSdcbiAgbG9nZmxhcmVfZnVuY3Rpb25zOlxuICAgIHR5cGU6ICdodHRwJ1xuICAgIGlucHV0czpcbiAgICAgIC0gcm91dGVyLmZ1bmN0aW9uc1xuICAgIGVuY29kaW5nOlxuICAgICAgY29kZWM6ICdqc29uJ1xuICAgIG1ldGhvZDogJ3Bvc3QnXG4gICAgcmVxdWVzdDpcbiAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMFxuICAgIHVyaTogJ2h0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMC9hcGkvbG9ncz9zb3VyY2VfbmFtZT1kZW5vLXJlbGF5LWxvZ3MmYXBpX2tleT0ke0xPR0ZMQVJFX0FQSV9LRVk\/TE9HRkxBUkVfQVBJX0tFWSBpcyByZXF1aXJlZH0nXG4gIGxvZ2ZsYXJlX3N0b3JhZ2U6XG4gICAgdHlwZTogJ2h0dHAnXG4gICAgaW5wdXRzOlxuICAgICAgLSBzdG9yYWdlX2xvZ3NcbiAgICBlbmNvZGluZzpcbiAgICAgIGNvZGVjOiAnanNvbidcbiAgICBtZXRob2Q6ICdwb3N0J1xuICAgIHJlcXVlc3Q6XG4gICAgICByZXRyeV9tYXhfZHVyYXRpb25fc2VjczogMTBcbiAgICB1cmk6ICdodHRwOi8vc3VwYWJhc2UtYW5hbHl0aWNzOjQwMDAvYXBpL2xvZ3M\/c291cmNlX25hbWU9c3RvcmFnZS5sb2dzLnByb2QuMiZhcGlfa2V5PSR7TE9HRkxBUkVfQVBJX0tFWT9MT0dGTEFSRV9BUElfS0VZIGlzIHJlcXVpcmVkfSdcbiAgbG9nZmxhcmVfa29uZzpcbiAgICB0eXBlOiAnaHR0cCdcbiAgICBpbnB1dHM6XG4gICAgICAtIGtvbmdfbG9nc1xuICAgICAgLSBrb25nX2VyclxuICAgIGVuY29kaW5nOlxuICAgICAgY29kZWM6ICdqc29uJ1xuICAgIG1ldGhvZDogJ3Bvc3QnXG4gICAgcmVxdWVzdDpcbiAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMFxuICAgIHVyaTogJ2h0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMC9hcGkvbG9ncz9zb3VyY2VfbmFtZT1jbG91ZGZsYXJlLmxvZ3MucHJvZCZhcGlfa2V5PSR7TE9HRkxBUkVfQVBJX0tFWT9MT0dGTEFSRV9BUElfS0VZIGlzIHJlcXVpcmVkfSdcbiIKICAgICAgLSAnL3Zhci9ydW4vZG9ja2VyLnNvY2s6L3Zhci9ydW4vZG9ja2VyLnNvY2s6cm8nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTE9HRkxBUkVfQVBJX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTE9HRkxBUkV9JwogICAgY29tbWFuZDoKICAgICAgLSAnLS1jb25maWcnCiAgICAgIC0gZXRjL3ZlY3Rvci92ZWN0b3IueW1sCiAgc3VwYWJhc2UtcmVzdDoKICAgIGltYWdlOiAncG9zdGdyZXN0L3Bvc3RncmVzdDp2MTIuMC4xJwogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtZGI6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgc3VwYWJhc2UtYW5hbHl0aWNzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BHUlNUX0RCX1VSST1wb3N0Z3JlczovL2F1dGhlbnRpY2F0b3I6JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfUAke1BPU1RHUkVTX0hPU1Q6LXN1cGFiYXNlLWRifToke1BPU1RHUkVTX1BPUlQ6LTU0MzJ9LyR7UE9TVEdSRVNfREI6LXBvc3RncmVzfScKICAgICAgLSAnUEdSU1RfREJfU0NIRU1BUz0ke1BHUlNUX0RCX1NDSEVNQVM6LXB1YmxpY30nCiAgICAgIC0gUEdSU1RfREJfQU5PTl9ST0xFPWFub24KICAgICAgLSAnUEdSU1RfSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfScKICAgICAgLSBQR1JTVF9EQl9VU0VfTEVHQUNZX0dVQ1M9ZmFsc2UKICAgICAgLSAnUEdSU1RfQVBQX1NFVFRJTkdTX0pXVF9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX0pXVH0nCiAgICAgIC0gJ1BHUlNUX0FQUF9TRVRUSU5HU19KV1RfRVhQPSR7SldUX0VYUElSWTotMzYwMH0nCiAgICBjb21tYW5kOiBwb3N0Z3Jlc3QKICAgIGV4Y2x1ZGVfZnJvbV9oYzogdHJ1ZQogIHN1cGFiYXNlLWF1dGg6CiAgICBpbWFnZTogJ3N1cGFiYXNlL2dvdHJ1ZTp2Mi4xNTEuMCcKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWRiOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHN1cGFiYXNlLWFuYWx5dGljczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHdnZXQKICAgICAgICAtICctLW5vLXZlcmJvc2UnCiAgICAgICAgLSAnLS10cmllcz0xJwogICAgICAgIC0gJy0tc3BpZGVyJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6OTk5OS9oZWFsdGgnCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIGludGVydmFsOiA1cwogICAgICByZXRyaWVzOiAzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBHT1RSVUVfQVBJX0hPU1Q9MC4wLjAuMAogICAgICAtIEdPVFJVRV9BUElfUE9SVD05OTk5CiAgICAgIC0gJ0FQSV9FWFRFUk5BTF9VUkw9JHtBUElfRVhURVJOQUxfVVJMOi1odHRwOi8vc3VwYWJhc2Uta29uZzo4MDAwfScKICAgICAgLSBHT1RSVUVfREJfRFJJVkVSPXBvc3RncmVzCiAgICAgIC0gJ0dPVFJVRV9EQl9EQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly9zdXBhYmFzZV9hdXRoX2FkbWluOiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AJHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn06JHtQT1NUR1JFU19QT1JUOi01NDMyfS8ke1BPU1RHUkVTX0RCOi1wb3N0Z3Jlc30nCiAgICAgIC0gJ0dPVFJVRV9TSVRFX1VSTD0ke1NFUlZJQ0VfRlFETl9TVVBBQkFTRUtPTkd9JwogICAgICAtICdHT1RSVUVfVVJJX0FMTE9XX0xJU1Q9JHtBRERJVElPTkFMX1JFRElSRUNUX1VSTFN9JwogICAgICAtICdHT1RSVUVfRElTQUJMRV9TSUdOVVA9JHtESVNBQkxFX1NJR05VUDotZmFsc2V9JwogICAgICAtIEdPVFJVRV9KV1RfQURNSU5fUk9MRVM9c2VydmljZV9yb2xlCiAgICAgIC0gR09UUlVFX0pXVF9BVUQ9YXV0aGVudGljYXRlZAogICAgICAtIEdPVFJVRV9KV1RfREVGQVVMVF9HUk9VUF9OQU1FPWF1dGhlbnRpY2F0ZWQKICAgICAgLSAnR09UUlVFX0pXVF9FWFA9JHtKV1RfRVhQSVJZOi0zNjAwfScKICAgICAgLSAnR09UUlVFX0pXVF9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX0pXVH0nCiAgICAgIC0gJ0dPVFJVRV9FWFRFUk5BTF9FTUFJTF9FTkFCTEVEPSR7RU5BQkxFX0VNQUlMX1NJR05VUDotdHJ1ZX0nCiAgICAgIC0gJ0dPVFJVRV9FWFRFUk5BTF9BTk9OWU1PVVNfVVNFUlNfRU5BQkxFRD0ke0VOQUJMRV9BTk9OWU1PVVNfVVNFUlM6LWZhbHNlfScKICAgICAgLSAnR09UUlVFX01BSUxFUl9BVVRPQ09ORklSTT0ke0VOQUJMRV9FTUFJTF9BVVRPQ09ORklSTTotZmFsc2V9JwogICAgICAtICdHT1RSVUVfU01UUF9BRE1JTl9FTUFJTD0ke1NNVFBfQURNSU5fRU1BSUx9JwogICAgICAtICdHT1RSVUVfU01UUF9IT1NUPSR7U01UUF9IT1NUfScKICAgICAgLSAnR09UUlVFX1NNVFBfUE9SVD0ke1NNVFBfUE9SVDotNTg3fScKICAgICAgLSAnR09UUlVFX1NNVFBfVVNFUj0ke1NNVFBfVVNFUn0nCiAgICAgIC0gJ0dPVFJVRV9TTVRQX1BBU1M9JHtTTVRQX1BBU1N9JwogICAgICAtICdHT1RSVUVfU01UUF9TRU5ERVJfTkFNRT0ke1NNVFBfU0VOREVSX05BTUV9JwogICAgICAtICdHT1RSVUVfTUFJTEVSX1VSTFBBVEhTX0lOVklURT0ke01BSUxFUl9VUkxQQVRIU19JTlZJVEU6LS9hdXRoL3YxL3ZlcmlmeX0nCiAgICAgIC0gJ0dPVFJVRV9NQUlMRVJfVVJMUEFUSFNfQ09ORklSTUFUSU9OPSR7TUFJTEVSX1VSTFBBVEhTX0NPTkZJUk1BVElPTjotL2F1dGgvdjEvdmVyaWZ5fScKICAgICAgLSAnR09UUlVFX01BSUxFUl9VUkxQQVRIU19SRUNPVkVSWT0ke01BSUxFUl9VUkxQQVRIU19SRUNPVkVSWTotL2F1dGgvdjEvdmVyaWZ5fScKICAgICAgLSAnR09UUlVFX01BSUxFUl9VUkxQQVRIU19FTUFJTF9DSEFOR0U9JHtNQUlMRVJfVVJMUEFUSFNfRU1BSUxfQ0hBTkdFOi0vYXV0aC92MS92ZXJpZnl9JwogICAgICAtICdHT1RSVUVfTUFJTEVSX1RFTVBMQVRFU19JTlZJVEU9JHtNQUlMRVJfVEVNUExBVEVTX0lOVklURX0nCiAgICAgIC0gJ0dPVFJVRV9NQUlMRVJfVEVNUExBVEVTX0NPTkZJUk1BVElPTj0ke01BSUxFUl9URU1QTEFURVNfQ09ORklSTUFUSU9OfScKICAgICAgLSAnR09UUlVFX01BSUxFUl9URU1QTEFURVNfUkVDT1ZFUlk9JHtNQUlMRVJfVEVNUExBVEVTX1JFQ09WRVJZfScKICAgICAgLSAnR09UUlVFX01BSUxFUl9URU1QTEFURVNfTUFHSUNfTElOSz0ke01BSUxFUl9URU1QTEFURVNfTUFHSUNfTElOS30nCiAgICAgIC0gJ0dPVFJVRV9NQUlMRVJfVEVNUExBVEVTX0VNQUlMX0NIQU5HRT0ke01BSUxFUl9URU1QTEFURVNfRU1BSUxfQ0hBTkdFfScKICAgICAgLSAnR09UUlVFX01BSUxFUl9TVUJKRUNUU19DT05GSVJNQVRJT049JHtNQUlMRVJfU1VCSkVDVFNfQ09ORklSTUFUSU9OfScKICAgICAgLSAnR09UUlVFX01BSUxFUl9TVUJKRUNUU19SRUNPVkVSWT0ke01BSUxFUl9TVUJKRUNUU19SRUNPVkVSWX0nCiAgICAgIC0gJ0dPVFJVRV9NQUlMRVJfU1VCSkVDVFNfTUFHSUNfTElOSz0ke01BSUxFUl9TVUJKRUNUU19NQUdJQ19MSU5LfScKICAgICAgLSAnR09UUlVFX01BSUxFUl9TVUJKRUNUU19FTUFJTF9DSEFOR0U9JHtNQUlMRVJfU1VCSkVDVFNfRU1BSUxfQ0hBTkdFfScKICAgICAgLSAnR09UUlVFX01BSUxFUl9TVUJKRUNUU19JTlZJVEU9JHtNQUlMRVJfU1VCSkVDVFNfSU5WSVRFfScKICAgICAgLSAnR09UUlVFX0VYVEVSTkFMX1BIT05FX0VOQUJMRUQ9JHtFTkFCTEVfUEhPTkVfU0lHTlVQOi10cnVlfScKICAgICAgLSAnR09UUlVFX1NNU19BVVRPQ09ORklSTT0ke0VOQUJMRV9QSE9ORV9BVVRPQ09ORklSTTotdHJ1ZX0nCiAgcmVhbHRpbWUtZGV2OgogICAgaW1hZ2U6ICdzdXBhYmFzZS9yZWFsdGltZTp2Mi4yOC4zMicKICAgIGNvbnRhaW5lcl9uYW1lOiByZWFsdGltZS1kZXYuc3VwYWJhc2UtcmVhbHRpbWUKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWRiOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHN1cGFiYXNlLWFuYWx5dGljczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctc1NmTCcKICAgICAgICAtICctLWhlYWQnCiAgICAgICAgLSAnLW8nCiAgICAgICAgLSAvZGV2L251bGwKICAgICAgICAtICctSCcKICAgICAgICAtICdBdXRob3JpemF0aW9uOiBCZWFyZXIgJHtTRVJWSUNFX1NVUEFCQVNFQU5PTl9LRVl9JwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6NDAwMC9hcGkvdGVuYW50cy9yZWFsdGltZS1kZXYvaGVhbHRoJwogICAgICB0aW1lb3V0OiA1cwogICAgICBpbnRlcnZhbDogNXMKICAgICAgcmV0cmllczogMwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9SVD00MDAwCiAgICAgIC0gJ0RCX0hPU1Q9JHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn0nCiAgICAgIC0gJ0RCX1BPUlQ9JHtQT1NUR1JFU19QT1JUOi01NDMyfScKICAgICAgLSBEQl9VU0VSPXN1cGFiYXNlX2FkbWluCiAgICAgIC0gJ0RCX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gJ0RCX05BTUU9JHtQT1NUR1JFU19EQjotcG9zdGdyZXN9JwogICAgICAtICdEQl9BRlRFUl9DT05ORUNUX1FVRVJZPVNFVCBzZWFyY2hfcGF0aCBUTyBfcmVhbHRpbWUnCiAgICAgIC0gREJfRU5DX0tFWT1zdXBhYmFzZXJlYWx0aW1lCiAgICAgIC0gJ0FQSV9KV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF9KV1R9JwogICAgICAtIEZMWV9BTExPQ19JRD1mbHkxMjMKICAgICAgLSBGTFlfQVBQX05BTUU9cmVhbHRpbWUKICAgICAgLSAnU0VDUkVUX0tFWV9CQVNFPSR7U0VDUkVUX1BBU1NXT1JEX1JFQUxUSU1FfScKICAgICAgLSAnRVJMX0FGTEFHUz0tcHJvdG9fZGlzdCBpbmV0X3RjcCcKICAgICAgLSBFTkFCTEVfVEFJTFNDQUxFPWZhbHNlCiAgICAgIC0gIkROU19OT0RFUz0nJyIKICAgIGNvbW1hbmQ6ICJzaCAtYyBcIi9hcHAvYmluL21pZ3JhdGUgJiYgL2FwcC9iaW4vcmVhbHRpbWUgZXZhbCAnUmVhbHRpbWUuUmVsZWFzZS5zZWVkcyhSZWFsdGltZS5SZXBvKScgJiYgL2FwcC9iaW4vc2VydmVyXCJcbiIKICBzdXBhYmFzZS1taW5pbzoKICAgIGltYWdlOiBtaW5pby9taW5pbwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01JTklPX1JPT1RfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ01JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfScKICAgIGNvbW1hbmQ6ICdzZXJ2ZXIgLS1jb25zb2xlLWFkZHJlc3MgIjo5MDAxIiAvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAnc2xlZXAgNSAmJiBleGl0IDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogNQogICAgdm9sdW1lczoKICAgICAgLSAnLi92b2x1bWVzL3N0b3JhZ2U6L2RhdGEnCiAgbWluaW8tY3JlYXRlYnVja2V0OgogICAgaW1hZ2U6IG1pbmlvL21jCiAgICByZXN0YXJ0OiAnbm8nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfScKICAgICAgLSAnTUlOSU9fUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudHJ5cG9pbnQ6CiAgICAgIC0gL2VudHJ5cG9pbnQuc2gKICAgIHZvbHVtZXM6CiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2VudHJ5cG9pbnQuc2gKICAgICAgICB0YXJnZXQ6IC9lbnRyeXBvaW50LnNoCiAgICAgICAgY29udGVudDogIiMhL2Jpbi9zaFxuL3Vzci9iaW4vbWMgYWxpYXMgc2V0IHN1cGFiYXNlLW1pbmlvIGh0dHA6Ly9zdXBhYmFzZS1taW5pbzo5MDAwICR7TUlOSU9fUk9PVF9VU0VSfSAke01JTklPX1JPT1RfUEFTU1dPUkR9O1xuL3Vzci9iaW4vbWMgbWIgLS1pZ25vcmUtZXhpc3Rpbmcgc3VwYWJhc2UtbWluaW8vc3R1YjtcbmV4aXQgMFxuIgogIHN1cGFiYXNlLXN0b3JhZ2U6CiAgICBpbWFnZTogJ3N1cGFiYXNlL3N0b3JhZ2UtYXBpOnYxLjAuNicKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWRiOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHN1cGFiYXNlLXJlc3Q6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgICAgaW1ncHJveHk6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLS1uby12ZXJib3NlJwogICAgICAgIC0gJy0tdHJpZXM9MScKICAgICAgICAtICctLXNwaWRlcicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjUwMDAvc3RhdHVzJwogICAgICB0aW1lb3V0OiA1cwogICAgICBpbnRlcnZhbDogNXMKICAgICAgcmV0cmllczogMwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVkVSX1BPUlQ9NTAwMAogICAgICAtIFNFUlZFUl9SRUdJT049bG9jYWwKICAgICAgLSBNVUxUSV9URU5BTlQ9ZmFsc2UKICAgICAgLSAnQVVUSF9KV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF9KV1R9JwogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly9zdXBhYmFzZV9zdG9yYWdlX2FkbWluOiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AJHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn06JHtQT1NUR1JFU19QT1JUOi01NDMyfS8ke1BPU1RHUkVTX0RCOi1wb3N0Z3Jlc30nCiAgICAgIC0gREJfSU5TVEFMTF9ST0xFUz1mYWxzZQogICAgICAtIFNUT1JBR0VfQkFDS0VORD1zMwogICAgICAtIFNUT1JBR0VfUzNfQlVDS0VUPXN0dWIKICAgICAgLSAnU1RPUkFHRV9TM19FTkRQT0lOVD1odHRwOi8vc3VwYWJhc2UtbWluaW86OTAwMCcKICAgICAgLSBTVE9SQUdFX1MzX0ZPUkNFX1BBVEhfU1RZTEU9dHJ1ZQogICAgICAtIFNUT1JBR0VfUzNfUkVHSU9OPXVzLWVhc3QtMQogICAgICAtICdBV1NfQUNDRVNTX0tFWV9JRD0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ0FXU19TRUNSRVRfQUNDRVNTX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgICAtIFVQTE9BRF9GSUxFX1NJWkVfTElNSVQ9NTI0Mjg4MDAwCiAgICAgIC0gVVBMT0FEX0ZJTEVfU0laRV9MSU1JVF9TVEFOREFSRD01MjQyODgwMDAKICAgICAgLSBVUExPQURfU0lHTkVEX1VSTF9FWFBJUkFUSU9OX1RJTUU9MTIwCiAgICAgIC0gVFVTX1VSTF9QQVRIPS91cGxvYWQvcmVzdW1hYmxlCiAgICAgIC0gVFVTX01BWF9TSVpFPTM2MDAwMDAKICAgICAgLSBJTUFHRV9UUkFOU0ZPUk1BVElPTl9FTkFCTEVEPXRydWUKICAgICAgLSAnSU1HUFJPWFlfVVJMPWh0dHA6Ly9pbWdwcm94eTo4MDgwJwogICAgICAtIElNR1BST1hZX1JFUVVFU1RfVElNRU9VVD0xNQogICAgICAtIERBVEFCQVNFX1NFQVJDSF9QQVRIPXN0b3JhZ2UKICAgIHZvbHVtZXM6CiAgICAgIC0gJy4vdm9sdW1lcy9zdG9yYWdlOi92YXIvbGliL3N0b3JhZ2UnCiAgaW1ncHJveHk6CiAgICBpbWFnZTogJ2RhcnRoc2ltL2ltZ3Byb3h5OnYzLjguMCcKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBpbWdwcm94eQogICAgICAgIC0gaGVhbHRoCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIGludGVydmFsOiA1cwogICAgICByZXRyaWVzOiAzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBJTUdQUk9YWV9MT0NBTF9GSUxFU1lTVEVNX1JPT1Q9LwogICAgICAtIElNR1BST1hZX1VTRV9FVEFHPXRydWUKICAgICAgLSAnSU1HUFJPWFlfRU5BQkxFX1dFQlBfREVURUNUSU9OPSR7SU1HUFJPWFlfRU5BQkxFX1dFQlBfREVURUNUSU9OOi10cnVlfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJy4vdm9sdW1lcy9zdG9yYWdlOi92YXIvbGliL3N0b3JhZ2UnCiAgc3VwYWJhc2UtbWV0YToKICAgIGltYWdlOiAnc3VwYWJhc2UvcG9zdGdyZXMtbWV0YTp2MC44MC4wJwogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtZGI6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgc3VwYWJhc2UtYW5hbHl0aWNzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQR19NRVRBX1BPUlQ9ODA4MAogICAgICAtICdQR19NRVRBX0RCX0hPU1Q9JHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn0nCiAgICAgIC0gJ1BHX01FVEFfREJfUE9SVD0ke1BPU1RHUkVTX1BPUlQ6LTU0MzJ9JwogICAgICAtICdQR19NRVRBX0RCX05BTUU9JHtQT1NUR1JFU19EQjotcG9zdGdyZXN9JwogICAgICAtIFBHX01FVEFfREJfVVNFUj1zdXBhYmFzZV9hZG1pbgogICAgICAtICdQR19NRVRBX0RCX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgc3VwYWJhc2UtZWRnZS1mdW5jdGlvbnM6CiAgICBpbWFnZTogJ3N1cGFiYXNlL2VkZ2UtcnVudGltZTp2MS41My4zJwogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtYW5hbHl0aWNzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gZWNobwogICAgICAgIC0gJ0VkZ2UgRnVuY3Rpb25zIGlzIGhlYWx0aHknCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIGludGVydmFsOiA1cwogICAgICByZXRyaWVzOiAzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfScKICAgICAgLSAnU1VQQUJBU0VfVVJMPWh0dHA6Ly9zdXBhYmFzZS1rb25nOjgwMDAnCiAgICAgIC0gJ1NVUEFCQVNFX0FOT05fS0VZPSR7U0VSVklDRV9TVVBBQkFTRUFOT05fS0VZfScKICAgICAgLSAnU1VQQUJBU0VfU0VSVklDRV9ST0xFX0tFWT0ke1NFUlZJQ0VfU1VQQUJBU0VTRVJWSUNFX0tFWX0nCiAgICAgIC0gJ1NVUEFCQVNFX0RCX1VSTD1wb3N0Z3Jlc3FsOi8vcG9zdGdyZXM6JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfUAke1BPU1RHUkVTX0hPU1Q6LXN1cGFiYXNlLWRifToke1BPU1RHUkVTX1BPUlQ6LTU0MzJ9LyR7UE9TVEdSRVNfREI6LXBvc3RncmVzfScKICAgICAgLSAnVkVSSUZZX0pXVD0ke0ZVTkNUSU9OU19WRVJJRllfSldUOi1mYWxzZX0nCiAgICB2b2x1bWVzOgogICAgICAtICcuL3ZvbHVtZXMvZnVuY3Rpb25zOi9ob21lL2Rlbm8vZnVuY3Rpb25zJwogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi92b2x1bWVzL2Z1bmN0aW9ucy9tYWluL2luZGV4LnRzCiAgICAgICAgdGFyZ2V0OiAvaG9tZS9kZW5vL2Z1bmN0aW9ucy9tYWluL2luZGV4LnRzCiAgICAgICAgY29udGVudDogImltcG9ydCB7IHNlcnZlIH0gZnJvbSAnaHR0cHM6Ly9kZW5vLmxhbmQvc3RkQDAuMTMxLjAvaHR0cC9zZXJ2ZXIudHMnXG5pbXBvcnQgKiBhcyBqb3NlIGZyb20gJ2h0dHBzOi8vZGVuby5sYW5kL3gvam9zZUB2NC4xNC40L2luZGV4LnRzJ1xuXG5jb25zb2xlLmxvZygnbWFpbiBmdW5jdGlvbiBzdGFydGVkJylcblxuY29uc3QgSldUX1NFQ1JFVCA9IERlbm8uZW52LmdldCgnSldUX1NFQ1JFVCcpXG5jb25zdCBWRVJJRllfSldUID0gRGVuby5lbnYuZ2V0KCdWRVJJRllfSldUJykgPT09ICd0cnVlJ1xuXG5mdW5jdGlvbiBnZXRBdXRoVG9rZW4ocmVxOiBSZXF1ZXN0KSB7XG4gIGNvbnN0IGF1dGhIZWFkZXIgPSByZXEuaGVhZGVycy5nZXQoJ2F1dGhvcml6YXRpb24nKVxuICBpZiAoIWF1dGhIZWFkZXIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ01pc3NpbmcgYXV0aG9yaXphdGlvbiBoZWFkZXInKVxuICB9XG4gIGNvbnN0IFtiZWFyZXIsIHRva2VuXSA9IGF1dGhIZWFkZXIuc3BsaXQoJyAnKVxuICBpZiAoYmVhcmVyICE9PSAnQmVhcmVyJykge1xuICAgIHRocm93IG5ldyBFcnJvcihgQXV0aCBoZWFkZXIgaXMgbm90ICdCZWFyZXIge3Rva2VufSdgKVxuICB9XG4gIHJldHVybiB0b2tlblxufVxuXG5hc3luYyBmdW5jdGlvbiB2ZXJpZnlKV1Qoand0OiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgY29uc3QgZW5jb2RlciA9IG5ldyBUZXh0RW5jb2RlcigpXG4gIGNvbnN0IHNlY3JldEtleSA9IGVuY29kZXIuZW5jb2RlKEpXVF9TRUNSRVQpXG4gIHRyeSB7XG4gICAgYXdhaXQgam9zZS5qd3RWZXJpZnkoand0LCBzZWNyZXRLZXkpXG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGNvbnNvbGUuZXJyb3IoZXJyKVxuICAgIHJldHVybiBmYWxzZVxuICB9XG4gIHJldHVybiB0cnVlXG59XG5cbnNlcnZlKGFzeW5jIChyZXE6IFJlcXVlc3QpID0+IHtcbiAgaWYgKHJlcS5tZXRob2QgIT09ICdPUFRJT05TJyAmJiBWRVJJRllfSldUKSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHRva2VuID0gZ2V0QXV0aFRva2VuKHJlcSlcbiAgICAgIGNvbnN0IGlzVmFsaWRKV1QgPSBhd2FpdCB2ZXJpZnlKV1QodG9rZW4pXG5cbiAgICAgIGlmICghaXNWYWxpZEpXVCkge1xuICAgICAgICByZXR1cm4gbmV3IFJlc3BvbnNlKEpTT04uc3RyaW5naWZ5KHsgbXNnOiAnSW52YWxpZCBKV1QnIH0pLCB7XG4gICAgICAgICAgc3RhdHVzOiA0MDEsXG4gICAgICAgICAgaGVhZGVyczogeyAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nIH0sXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY29uc29sZS5lcnJvcihlKVxuICAgICAgcmV0dXJuIG5ldyBSZXNwb25zZShKU09OLnN0cmluZ2lmeSh7IG1zZzogZS50b1N0cmluZygpIH0pLCB7XG4gICAgICAgIHN0YXR1czogNDAxLFxuICAgICAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSxcbiAgICAgIH0pXG4gICAgfVxuICB9XG5cbiAgY29uc3QgdXJsID0gbmV3IFVSTChyZXEudXJsKVxuICBjb25zdCB7IHBhdGhuYW1lIH0gPSB1cmxcbiAgY29uc3QgcGF0aF9wYXJ0cyA9IHBhdGhuYW1lLnNwbGl0KCcvJylcbiAgY29uc3Qgc2VydmljZV9uYW1lID0gcGF0aF9wYXJ0c1sxXVxuXG4gIGlmICghc2VydmljZV9uYW1lIHx8IHNlcnZpY2VfbmFtZSA9PT0gJycpIHtcbiAgICBjb25zdCBlcnJvciA9IHsgbXNnOiAnbWlzc2luZyBmdW5jdGlvbiBuYW1lIGluIHJlcXVlc3QnIH1cbiAgICByZXR1cm4gbmV3IFJlc3BvbnNlKEpTT04uc3RyaW5naWZ5KGVycm9yKSwge1xuICAgICAgc3RhdHVzOiA0MDAsXG4gICAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSxcbiAgICB9KVxuICB9XG5cbiAgY29uc3Qgc2VydmljZVBhdGggPSBgL2hvbWUvZGVuby9mdW5jdGlvbnMvJHtzZXJ2aWNlX25hbWV9YFxuICBjb25zb2xlLmVycm9yKGBzZXJ2aW5nIHRoZSByZXF1ZXN0IHdpdGggJHtzZXJ2aWNlUGF0aH1gKVxuXG4gIGNvbnN0IG1lbW9yeUxpbWl0TWIgPSAxNTBcbiAgY29uc3Qgd29ya2VyVGltZW91dE1zID0gMSAqIDYwICogMTAwMFxuICBjb25zdCBub01vZHVsZUNhY2hlID0gZmFsc2VcbiAgY29uc3QgaW1wb3J0TWFwUGF0aCA9IG51bGxcbiAgY29uc3QgZW52VmFyc09iaiA9IERlbm8uZW52LnRvT2JqZWN0KClcbiAgY29uc3QgZW52VmFycyA9IE9iamVjdC5rZXlzKGVudlZhcnNPYmopLm1hcCgoaykgPT4gW2ssIGVudlZhcnNPYmpba11dKVxuXG4gIHRyeSB7XG4gICAgY29uc3Qgd29ya2VyID0gYXdhaXQgRWRnZVJ1bnRpbWUudXNlcldvcmtlcnMuY3JlYXRlKHtcbiAgICAgIHNlcnZpY2VQYXRoLFxuICAgICAgbWVtb3J5TGltaXRNYixcbiAgICAgIHdvcmtlclRpbWVvdXRNcyxcbiAgICAgIG5vTW9kdWxlQ2FjaGUsXG4gICAgICBpbXBvcnRNYXBQYXRoLFxuICAgICAgZW52VmFycyxcbiAgICB9KVxuICAgIHJldHVybiBhd2FpdCB3b3JrZXIuZmV0Y2gocmVxKVxuICB9IGNhdGNoIChlKSB7XG4gICAgY29uc3QgZXJyb3IgPSB7IG1zZzogZS50b1N0cmluZygpIH1cbiAgICByZXR1cm4gbmV3IFJlc3BvbnNlKEpTT04uc3RyaW5naWZ5KGVycm9yKSwge1xuICAgICAgc3RhdHVzOiA1MDAsXG4gICAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSxcbiAgICB9KVxuICB9XG59KSIKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vdm9sdW1lcy9mdW5jdGlvbnMvaGVsbG8vaW5kZXgudHMKICAgICAgICB0YXJnZXQ6IC9ob21lL2Rlbm8vZnVuY3Rpb25zL2hlbGxvL2luZGV4LnRzCiAgICAgICAgY29udGVudDogIi8vIEZvbGxvdyB0aGlzIHNldHVwIGd1aWRlIHRvIGludGVncmF0ZSB0aGUgRGVubyBsYW5ndWFnZSBzZXJ2ZXIgd2l0aCB5b3VyIGVkaXRvcjpcbi8vIGh0dHBzOi8vZGVuby5sYW5kL21hbnVhbC9nZXR0aW5nX3N0YXJ0ZWQvc2V0dXBfeW91cl9lbnZpcm9ubWVudFxuLy8gVGhpcyBlbmFibGVzIGF1dG9jb21wbGV0ZSwgZ28gdG8gZGVmaW5pdGlvbiwgZXRjLlxuXG5pbXBvcnQgeyBzZXJ2ZSB9IGZyb20gXCJodHRwczovL2Rlbm8ubGFuZC9zdGRAMC4xNzcuMS9odHRwL3NlcnZlci50c1wiXG5cbnNlcnZlKGFzeW5jICgpID0+IHtcbiAgcmV0dXJuIG5ldyBSZXNwb25zZShcbiAgICBgXCJIZWxsbyBmcm9tIEVkZ2UgRnVuY3Rpb25zIVwiYCxcbiAgICB7IGhlYWRlcnM6IHsgXCJDb250ZW50LVR5cGVcIjogXCJhcHBsaWNhdGlvbi9qc29uXCIgfSB9LFxuICApXG59KVxuXG4vLyBUbyBpbnZva2U6XG4vLyBjdXJsICdodHRwOi8vbG9jYWxob3N0OjxLT05HX0hUVFBfUE9SVD4vZnVuY3Rpb25zL3YxL2hlbGxvJyBcXFxuLy8gICAtLWhlYWRlciAnQXV0aG9yaXphdGlvbjogQmVhcmVyIDxhbm9uL3NlcnZpY2Vfcm9sZSBBUEkga2V5PidcbiIKICAgIGNvbW1hbmQ6CiAgICAgIC0gc3RhcnQKICAgICAgLSAnLS1tYWluLXNlcnZpY2UnCiAgICAgIC0gL2hvbWUvZGVuby9mdW5jdGlvbnMvbWFpbgo=","tags":["firebase","alternative","open-source"],"logo":"svgs\/supabase.svg","minversion":"4.0.0-beta.228","port":"8000"},"syncthing":{"documentation":"https:\/\/syncthing.net\/?utm_source=coolify.io","slogan":"Syncthing synchronizes files between two or more computers in real time.","compose":"c2VydmljZXM6CiAgc3luY3RoaW5nOgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL3N5bmN0aGluZzpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fU1lOQ1RISU5HXzgzODQKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdGMvVVRDCiAgICB2b2x1bWVzOgogICAgICAtICdzeW5jdGhpbmctY29uZmlnOi9jb25maWcnCiAgICAgIC0gJ3N5bmN0aGluZy1kYXRhMTovZGF0YTEnCiAgICAgIC0gJ3N5bmN0aGluZy1kYXRhMjovZGF0YTInCiAgICBwb3J0czoKICAgICAgLSAnMjIwMDA6MjIwMDAvdGNwJwogICAgICAtICcyMjAwMDoyMjAwMC91ZHAnCiAgICAgIC0gJzIxMDI3OjIxMDI3L3VkcCcK","tags":["filestorage","data","synchronization"],"logo":"svgs\/syncthing.svg","minversion":"0.0.0","port":"8384"},"tolgee":{"documentation":"https:\/\/tolgee.io\/?utm_source=coolify.io","slogan":"Tolgee is a localization management platform for developers and translators.","compose":"c2VydmljZXM6CiAgdG9sZ2VlOgogICAgaW1hZ2U6IHRvbGdlZS90b2xnZWUKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9UT0xHRUVfODA4MAogICAgICAtIFRPTEdFRV9BVVRIRU5USUNBVElPTl9FTkFCTEVEPXRydWUKICAgICAgLSBUT0xHRUVfQVVUSEVOVElDQVRJT05fSU5JVElBTF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9UT0xHRUUKICAgICAgLSBUT0xHRUVfQVVUSEVOVElDQVRJT05fSU5JVElBTF9VU0VSTkFNRT1hZG1pbgogICAgICAtIFRPTEdFRV9BVVRIRU5USUNBVElPTl9KV1RfU0VDUkVUPSRTRVJWSUNFX1BBU1NXT1JEX0pXVAogICAgICAtIFRPTEdFRV9QT1NUR1JFU19BVVRPU1RBUlRfRU5BQkxFRD1mYWxzZQogICAgICAtICdTUFJJTkdfREFUQVNPVVJDRV9VUkw9amRiYzpwb3N0Z3Jlc3FsOi8vcG9zdGdyZXNxbDo1NDMyLyR7UE9TVEdSRVNfREI6LXRvbGdlZX0nCiAgICAgIC0gJ1NQUklOR19EQVRBU09VUkNFX1VTRVJOQU1FPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdTUFJJTkdfREFUQVNPVVJDRV9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICB2b2x1bWVzOgogICAgICAtICd0b2xnZWUtZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLXEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MDgwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3Jlc3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAndG9sZ2VlLXBvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTUUxfREFUQUJBU0U6LXRvbGdlZX0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["localization","translation","management","platform"],"logo":"svgs\/tolgee.svg","minversion":"0.0.0","port":"8080"},"trigger-with-external-database":{"documentation":"https:\/\/trigger.dev?utm_source=coolify.io","slogan":"The open source Background Jobs framework for TypeScript","compose":"c2VydmljZXM6CiAgdHJpZ2dlcjoKICAgIGltYWdlOiAnZ2hjci5pby90cmlnZ2VyZG90ZGV2L3RyaWdnZXIuZGV2OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9UUklHR0VSXzMwMDAKICAgICAgLSBMT0dJTl9PUklHSU49JFNFUlZJQ0VfRlFETl9UUklHR0VSCiAgICAgIC0gQVBQX09SSUdJTj0kU0VSVklDRV9GUUROX1RSSUdHRVIKICAgICAgLSBNQUdJQ19MSU5LX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9NQUdJQwogICAgICAtIEVOQ1JZUFRJT05fS0VZPSRTRVJWSUNFX1BBU1NXT1JEXzY0X0VOQ1JZUFRJT04KICAgICAgLSBTRVNTSU9OX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9TRVNTSU9OCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD0ke0RBVEFCQVNFX1VSTH0nCiAgICAgIC0gJ0RJUkVDVF9VUkw9JHtEQVRBQkFTRV9VUkx9JwogICAgICAtIFJVTlRJTUVfUExBVEZPUk09ZG9ja2VyLWNvbXBvc2UKICAgICAgLSBOT0RFX0VOVj1wcm9kdWN0aW9uCiAgICAgIC0gJ0FVVEhfR0lUSFVCX0NMSUVOVF9JRD0ke0FVVEhfR0lUSFVCX0NMSUVOVF9JRH0nCiAgICAgIC0gJ0FVVEhfR0lUSFVCX0NMSUVOVF9TRUNSRVQ9JHtBVVRIX0dJVEhVQl9DTElFTlRfU0VDUkVUfScKICAgICAgLSAnUkVTRU5EX0FQSV9LRVk9JHtSRVNFTkRfQVBJX0tFWX0nCiAgICAgIC0gJ0ZST01fRU1BSUw9JHtGUk9NX0VNQUlMfScKICAgICAgLSAnUkVQTFlfVE9fRU1BSUw9JHtSRVBMWV9UT19FTUFJTH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIE5PTkUK","tags":["trigger.dev","background jobs","typescript","trigger","jobs","cron","scheduler"],"logo":"svgs\/trigger.png","minversion":"0.0.0","port":"3000"},"trigger":{"documentation":"https:\/\/trigger.dev?utm_source=coolify.io","slogan":"The open source Background Jobs framework for TypeScript","compose":"c2VydmljZXM6CiAgdHJpZ2dlcjoKICAgIGltYWdlOiAnZ2hjci5pby90cmlnZ2VyZG90ZGV2L3RyaWdnZXIuZGV2OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9UUklHR0VSXzMwMDAKICAgICAgLSBMT0dJTl9PUklHSU49JFNFUlZJQ0VfRlFETl9UUklHR0VSCiAgICAgIC0gQVBQX09SSUdJTj0kU0VSVklDRV9GUUROX1RSSUdHRVIKICAgICAgLSBNQUdJQ19MSU5LX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9NQUdJQwogICAgICAtIEVOQ1JZUFRJT05fS0VZPSRTRVJWSUNFX1BBU1NXT1JEXzY0X0VOQ1JZUFRJT04KICAgICAgLSBTRVNTSU9OX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9TRVNTSU9OCiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotdHJpZ2dlcn0nCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzcWw6NTQzMi8kUE9TVEdSRVNfREInCiAgICAgIC0gJ0RJUkVDVF9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzcWw6NTQzMi8kUE9TVEdSRVNfREInCiAgICAgIC0gUlVOVElNRV9QTEFURk9STT1kb2NrZXItY29tcG9zZQogICAgICAtIE5PREVfRU5WPXByb2R1Y3Rpb24KICAgICAgLSAnQVVUSF9HSVRIVUJfQ0xJRU5UX0lEPSR7QVVUSF9HSVRIVUJfQ0xJRU5UX0lEfScKICAgICAgLSAnQVVUSF9HSVRIVUJfQ0xJRU5UX1NFQ1JFVD0ke0FVVEhfR0lUSFVCX0NMSUVOVF9TRUNSRVR9JwogICAgICAtICdSRVNFTkRfQVBJX0tFWT0ke1JFU0VORF9BUElfS0VZfScKICAgICAgLSAnRlJPTV9FTUFJTD0ke0ZST01fRU1BSUx9JwogICAgICAtICdSRVBMWV9UT19FTUFJTD0ke1JFUExZX1RPX0VNQUlMfScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gTk9ORQogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi10cmlnZ2VyfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["trigger.dev","background jobs","typescript","trigger","jobs","cron","scheduler"],"logo":"svgs\/trigger.png","minversion":"0.0.0","port":"3000"},"twenty":{"documentation":"https:\/\/docs.twenty.com?utm_source=coolify.io","slogan":"Twenty is a CRM designed to fit your unique business needs.","compose":"c2VydmljZXM6CiAgdHdlbnR5OgogICAgaW1hZ2U6ICd0d2VudHljcm0vdHdlbnR5OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9UUklHR0VSXzMwMDAKICAgICAgLSBTRVJWRVJfVVJMPSRTRVJWSUNFX0ZRRE5fVFdFTlRZCiAgICAgIC0gRlJPTlRfQkFTRV9VUkw9JFNFUlZJQ0VfRlFETl9UV0VOVFkKICAgICAgLSBFTkFCTEVfREJfTUlHUkFUSU9OUz10cnVlCiAgICAgIC0gU0lHTl9JTl9QUkVGSUxMRUQ9ZmFsc2UKICAgICAgLSAnU1RPUkFHRV9UWVBFPSR7U1RPUkFHRV9UWVBFOi1sb2NhbH0nCiAgICAgIC0gU1RPUkFHRV9TM19SRUdJT049JFNUT1JBR0VfUzNfUkVHSU9OCiAgICAgIC0gU1RPUkFHRV9TM19OQU1FPSRTVE9SQUdFX1MzX05BTUUKICAgICAgLSBTVE9SQUdFX1MzX0VORFBPSU5UPSRTVE9SQUdFX1MzX0VORFBPSU5UCiAgICAgIC0gQUNDRVNTX1RPS0VOX1NFQ1JFVD0kU0VSVklDRV9CQVNFNjRfMzJfQUNDRVNTCiAgICAgIC0gTE9HSU5fVE9LRU5fU0VDUkVUPSRTRVJWSUNFX0JBU0U2NF8zMl9MT0dJTgogICAgICAtIFJFRlJFU0hfVE9LRU5fU0VDUkVUPSRTRVJWSUNFX0JBU0U2NF8zMl9SRUZSRVNICiAgICAgIC0gRklMRV9UT0tFTl9TRUNSRVQ9JFNFUlZJQ0VfQkFTRTY0XzMyX0ZJTEUKICAgICAgLSBQT1NUR1JFU19BRE1JTl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdQR19EQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly9wb3N0Z3JlczokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0Bwb3N0Z3Jlczo1NDMyL2RlZmF1bHQnCiAgICAgIC0gRU1BSUxfRlJPTV9BRERSRVNTPSRFTUFJTF9GUk9NX0FERFJFU1MKICAgICAgLSBFTUFJTF9GUk9NX05BTUU9JEVNQUlMX0ZST01fTkFNRQogICAgICAtIEVNQUlMX1NZU1RFTV9BRERSRVNTPSRFTUFJTF9TWVNURU1fQUREUkVTUwogICAgICAtICdFTUFJTF9EUklWRVI9JHtFTUFJTF9EUklWRVI6LWxvZ2dlcn0nCiAgICAgIC0gRU1BSUxfU01UUF9IT1NUPSRFTUFJTF9TTVRQX0hPU1QKICAgICAgLSBFTUFJTF9TTVRQX1BPUlQ9JEVNQUlMX1NNVFBfUE9SVAogICAgICAtIEVNQUlMX1NNVFBfVVNFUj0kRU1BSUxfU01UUF9VU0VSCiAgICAgIC0gRU1BSUxfU01UUF9QQVNTV09SRD0kRU1BSUxfU01UUF9QQVNTV09SRAogICAgICAtICdURUxFTUVUUllfRU5BQkxFRD0ke1RFTEVNRVRSWV9FTkFCTEVEOi1mYWxzZX0nCiAgICAgIC0gJ0NBQ0hFX1NUT1JBR0VfVFlQRT0ke0NBQ0hFX1NUT1JBR0VfVFlQRTotcmVkaXN9JwogICAgICAtIFJFRElTX0hPU1Q9cmVkaXMKICAgICAgLSBSRURJU19QT1JUPTYzNzkKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMC9oZWFsdGh6JwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3R3ZW50eWNybS90d2VudHktcG9zdGdyZXM6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfREI9ZGVmYXVsdAogICAgdm9sdW1lczoKICAgICAgLSAncGctZGF0YTovYml0bmFtaS9wb3N0Z3Jlc3FsJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHJlZGlzOgogICAgaW1hZ2U6ICdyZWRpczpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdyZWRpcy1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["crm","self-hosted","dashboard"],"logo":"svgs\/twenty.svg","minversion":"0.0.0","port":"3000"},"umami":{"documentation":"https:\/\/umami.is?utm_source=coolify.io","slogan":"Umami is web analytics platform which provides insights into visitor behavior without compromising user privacy.","compose":"c2VydmljZXM6CiAgdW1hbWk6CiAgICBpbWFnZTogJ2doY3IuaW8vdW1hbWktc29mdHdhcmUvdW1hbWk6cG9zdGdyZXNxbC1sYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVU1BTUlfMzAwMAogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzcWw6NTQzMi8kUE9TVEdSRVNfREInCiAgICAgIC0gREFUQUJBU0VfVFlQRT1wb3N0Z3JlcwogICAgICAtIEFQUF9TRUNSRVQ9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfVU1BTUkKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwL2FwaS9oZWFydGJlYXQnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotdW1hbWl9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["analytics","insights","privacy"],"logo":"svgs\/umami.svg","minversion":"0.0.0","port":"3000"},"unleash-with-postgresql":{"documentation":"https:\/\/docs.getunleash.io?utm_source=coolify.io","slogan":"Open source feature flag management for enterprises.","compose":"c2VydmljZXM6CiAgdW5sZWFzaDoKICAgIGltYWdlOiAndW5sZWFzaG9yZy91bmxlYXNoLXNlcnZlcjpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVU5MRUFTSF80MjQyCiAgICAgIC0gJ1VOTEVBU0hfVVJMPSR7U0VSVklDRV9GUUROX1VOTEVBU0h9JwogICAgICAtICdVTkxFQVNIX0RFRkFVTFRfQURNSU5fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1VOTEVBU0h9JwogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzL2RiJwogICAgICAtIERBVEFCQVNFX1NTTD1mYWxzZQogICAgICAtIExPR19MRVZFTD13YXJuCiAgICAgIC0gJ0lOSVRfRlJPTlRFTkRfQVBJX1RPS0VOUz1kZWZhdWx0OmRlZmF1bHQ6ZGV2ZWxvcG1lbnQudW5sZWFzaC1pbnNlY3VyZS1mcm9udGVuZC1hcGktdG9rZW4nCiAgICAgIC0gJ0lOSVRfQ0xJRU5UX0FQSV9UT0tFTlM9ZGVmYXVsdDpkZXZlbG9wbWVudC51bmxlYXNoLWluc2VjdXJlLWFwaS10b2tlbicKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBjb21tYW5kOgogICAgICAtIG5vZGUKICAgICAgLSBpbmRleC5qcwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6ICd3Z2V0IC0tbm8tdmVyYm9zZSAtLXRyaWVzPTEgLS1zcGlkZXIgaHR0cDovLzEyNy4wLjAuMTo0MjQyL2hlYWx0aCB8fCBleGl0IDEnCiAgICAgIGludGVydmFsOiAxcwogICAgICB0aW1lb3V0OiAxbQogICAgICByZXRyaWVzOiA1CiAgICAgIHN0YXJ0X3BlcmlvZDogMTVzCiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE1JwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfREI9ZGIKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBwZ19pc3JlYWR5CiAgICAgICAgLSAnLS11c2VybmFtZT0kU0VSVklDRV9VU0VSX1BPU1RHUkVTJwogICAgICAgIC0gJy0taG9zdD0xMjcuMC4wLjEnCiAgICAgICAgLSAnLS1wb3J0PTU0MzInCiAgICAgICAgLSAnLS1kYm5hbWU9ZGInCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxbQogICAgICByZXRyaWVzOiA1CiAgICAgIHN0YXJ0X3BlcmlvZDogMTBzCg==","tags":["unleash","feature flags","feature toggles","ab testing","open source"],"logo":"svgs\/unleash.svg","minversion":"0.0.0","port":"4242"},"unleash-without-database":{"documentation":"https:\/\/docs.getunleash.io?utm_source=coolify.io","slogan":"Open source feature flag management for enterprises.","compose":"c2VydmljZXM6CiAgdW5sZWFzaDoKICAgIGltYWdlOiAndW5sZWFzaG9yZy91bmxlYXNoLXNlcnZlcjpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVU5MRUFTSF80MjQyCiAgICAgIC0gJ1VOTEVBU0hfVVJMPSR7U0VSVklDRV9GUUROX1VOTEVBU0h9JwogICAgICAtICdVTkxFQVNIX0RFRkFVTFRfQURNSU5fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1VOTEVBU0h9JwogICAgICAtICdEQVRBQkFTRV9VUkw9JHtEQVRBQkFTRV9VUkx9JwogICAgICAtICdEQVRBQkFTRV9TU0w9JHtEQVRBQkFTRV9TU0w6LWZhbHNlfScKICAgICAgLSBMT0dfTEVWRUw9d2FybgogICAgICAtICdJTklUX0ZST05URU5EX0FQSV9UT0tFTlM9ZGVmYXVsdDpkZWZhdWx0OmRldmVsb3BtZW50LnVubGVhc2gtaW5zZWN1cmUtZnJvbnRlbmQtYXBpLXRva2VuJwogICAgICAtICdJTklUX0NMSUVOVF9BUElfVE9LRU5TPWRlZmF1bHQ6ZGV2ZWxvcG1lbnQudW5sZWFzaC1pbnNlY3VyZS1hcGktdG9rZW4nCiAgICBjb21tYW5kOgogICAgICAtIG5vZGUKICAgICAgLSBpbmRleC5qcwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6ICd3Z2V0IC0tbm8tdmVyYm9zZSAtLXRyaWVzPTEgLS1zcGlkZXIgaHR0cDovLzEyNy4wLjAuMTo0MjQyL2hlYWx0aCB8fCBleGl0IDEnCiAgICAgIGludGVydmFsOiAxcwogICAgICB0aW1lb3V0OiAxbQogICAgICByZXRyaWVzOiA1CiAgICAgIHN0YXJ0X3BlcmlvZDogMTVzCg==","tags":["unleash","feature flags","feature toggles","ab testing","open source"],"logo":"svgs\/unleash.svg","minversion":"0.0.0","port":"4242"},"uptime-kuma":{"documentation":"https:\/\/github.com\/louislam\/uptime-kuma?tab=readme-ov-file?utm_source=coolify.io","slogan":"Uptime Kuma is a monitoring tool for tracking the status and performance of your applications in real-time.","compose":"c2VydmljZXM6CiAgdXB0aW1lLWt1bWE6CiAgICBpbWFnZTogJ2xvdWlzbGFtL3VwdGltZS1rdW1hOjEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVVBUSU1FLUtVTUFfMzAwMQogICAgdm9sdW1lczoKICAgICAgLSAndXB0aW1lLWt1bWE6L2FwcC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtIGV4dHJhL2hlYWx0aGNoZWNrCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["monitoring","status","performance","web","services","applications","real-time"],"logo":"svgs\/uptime-kuma.svg","minversion":"0.0.0","port":"3001"},"vaultwarden":{"documentation":"https:\/\/github.com\/dani-garcia\/vaultwarden?utm_source=coolify.io","slogan":"Vaultwarden is a password manager that allows you to securely store and manage your passwords.","compose":"c2VydmljZXM6CiAgdmF1bHR3YXJkZW46CiAgICBpbWFnZTogJ3ZhdWx0d2FyZGVuL3NlcnZlcjpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVkFVTFRXQVJERU4KICAgICAgLSAnRE9NQUlOPSR7U0VSVklDRV9GUUROX1ZBVUxUV0FSREVOfScKICAgICAgLSAnREFUQUJBU0VfVVJMPSR7VkFVTFRXQVJERU5fREJfVVJMOi1kYXRhL2RiLnNxbGl0ZTN9JwogICAgICAtICdTSUdOVVBTX0FMTE9XRUQ9JHtTSUdOVVBfQUxMT1dFRDotdHJ1ZX0nCiAgICAgIC0gJ0FETUlOX1RPS0VOPSR7U0VSVklDRV9QQVNTV09SRF82NF9BRE1JTn0nCiAgICAgIC0gSVBfSEVBREVSPVgtRm9yd2FyZGVkLUZvcgogICAgICAtICdQVVNIX0VOQUJMRUQ9JHtQVVNIX0VOQUJMRUQ6LWZhbHNlfScKICAgICAgLSAnUFVTSF9JTlNUQUxMQVRJT05fSUQ9JHtQVVNIX1NFUlZJQ0VfSUR9JwogICAgICAtICdQVVNIX0lOU1RBTExBVElPTl9LRVk9JHtQVVNIX1NFUlZJQ0VfS0VZfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3ZhdWx0d2FyZGVuLWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["password manager","security"],"logo":"svgs\/bitwarden.svg","minversion":"0.0.0","port":"80"},"vikunja":{"documentation":"https:\/\/vikunja.io?utm_source=coolify.io","slogan":"The open-source, self-hostable to-do app. Organize everything, on all platforms.","compose":"c2VydmljZXM6CiAgdmlrdW5qYToKICAgIGltYWdlOiB2aWt1bmphL3Zpa3VuamEKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9WSUtVTkpBCiAgICAgIC0gVklLVU5KQV9TRVJWSUNFX1BVQkxJQ1VSTD0kU0VSVklDRV9GUUROX1ZJS1VOSkEKICAgICAgLSBWSUtVTkpBX1NFUlZJQ0VfSldUU0VDUkVUPSRTRVJWSUNFX1BBU1NXT1JEX0pXVFNFQ1JFVAogICAgICAtIFZJS1VOSkFfU0VSVklDRV9FTkFCTEVSRUdJU1RSQVRJT049dHJ1ZQogICAgdm9sdW1lczoKICAgICAgLSAndmlrdW5qYS1kYXRhOi9hcHAvdmlrdW5qYS8nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gd2dldAogICAgICAgIC0gJy0tc3BpZGVyJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzQ1NicKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=","tags":["productivity","todo"],"logo":"svgs\/vikunja.svg","minversion":"0.0.0","port":"3456"},"weblate":{"documentation":"https:\/\/weblate.org?utm_source=coolify.io","slogan":"Weblate is a libre software web-based continuous localization system.","compose":"c2VydmljZXM6CiAgd2VibGF0ZToKICAgIGltYWdlOiAnd2VibGF0ZS93ZWJsYXRlOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9XRUJMQVRFXzgwODAKICAgICAgLSBXRUJMQVRFX1NJVEVfRE9NQUlOPSRTRVJWSUNFX1VSTF9XRUJMQVRFCiAgICAgIC0gJ1dFQkxBVEVfQURNSU5fTkFNRT0ke1dFQkxBVEVfQURNSU5fTkFNRTotQWRtaW59JwogICAgICAtICdXRUJMQVRFX0FETUlOX0VNQUlMPSR7V0VCTEFURV9BRE1JTl9FTUFJTDotYWRtaW5AZXhhbXBsZS5jb219JwogICAgICAtIFdFQkxBVEVfQURNSU5fUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfV0VCTEFURQogICAgICAtICdERUZBVUxUX0ZST01fRU1BSUw9JHtXRUJMQVRFX0FETUlOX0VNQUlMOi1hZG1pbkBleGFtcGxlLmNvbX0nCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREFUQUJBU0U9JHtQT1NUR1JFU19EQjotd2VibGF0ZX0nCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3Jlc3FsCiAgICAgIC0gUE9TVEdSRVNfUE9SVD01NDMyCiAgICAgIC0gUkVESVNfSE9TVD1yZWRpcwogICAgICAtIFJFRElTX1BPUlQ9NjM3OQogICAgICAtIFJFRElTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1JFRElTCiAgICB2b2x1bWVzOgogICAgICAtICd3ZWJsYXRlLWRhdGE6L2FwcC9kYXRhJwogICAgICAtICd3ZWJsYXRlLWNhY2hlOi9hcHAvY2FjaGUnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODA4MCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAzMAogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi13ZWJsYXRlfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6Ny1hbHBpbmUnCiAgICBjb21tYW5kOiAiLS1hcHBlbmRvbmx5IHllcyAtLXJlcXVpcmVwYXNzICR7U0VSVklDRV9QQVNTV09SRF9SRURJU31cbiIKICAgIGVudmlyb25tZW50OgogICAgICAtIFJFRElTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1JFRElTCiAgICB2b2x1bWVzOgogICAgICAtICd3ZWJsYXRlLXJlZGlzLWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK","tags":["localization","translation","web","web-based","continuous","libre","software"],"logo":"svgs\/weblate.webp","minversion":"0.0.0","port":"8080"},"whoogle":{"documentation":"https:\/\/github.com\/benbusby\/whoogle-search?tab=readme-ov-file?utm_source=coolify.io","slogan":"Whoogle is a self-hosted, privacy-focused search engine front-end for accessing Google search results without tracking and data collection.","compose":"c2VydmljZXM6CiAgd2hvb2dsZToKICAgIGltYWdlOiAnYmVuYnVzYnkvd2hvb2dsZS1zZWFyY2g6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1dIT09HTEVfNTAwMAogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjUwMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK","tags":["privacy","search engine"],"logo":"svgs\/unknown.svg","minversion":"0.0.0","port":"5000"},"wordpress-with-mariadb":{"documentation":"https:\/\/wordpress.org?utm_source=coolify.io","slogan":"Wordpress is open source software you can use to create a beautiful website, blog, or app.","compose":"c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fV09SRFBSRVNTCiAgICAgIC0gV09SRFBSRVNTX0RCX0hPU1Q9bWFyaWFkYgogICAgICAtIFdPUkRQUkVTU19EQl9VU0VSPSRTRVJWSUNFX1VTRVJfV09SRFBSRVNTCiAgICAgIC0gV09SRFBSRVNTX0RCX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1dPUkRQUkVTUwogICAgICAtIFdPUkRQUkVTU19EQl9OQU1FPXdvcmRwcmVzcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBtYXJpYWRiCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjEnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTAKICBtYXJpYWRiOgogICAgaW1hZ2U6ICdtYXJpYWRiOjExJwogICAgdm9sdW1lczoKICAgICAgLSAnbWFyaWFkYi1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTVlTUUxfUk9PVF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9ST09UCiAgICAgIC0gTVlTUUxfREFUQUJBU0U9d29yZHByZXNzCiAgICAgIC0gTVlTUUxfVVNFUj0kU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICAtIE1ZU1FMX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1dPUkRQUkVTUwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGhlYWx0aGNoZWNrLnNoCiAgICAgICAgLSAnLS1jb25uZWN0JwogICAgICAgIC0gJy0taW5ub2RiX2luaXRpYWxpemVkJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["cms","blog","content","management","mariadb"],"logo":"svgs\/wordpress.svg","minversion":"0.0.0"},"wordpress-with-mysql":{"documentation":"https:\/\/wordpress.org?utm_source=coolify.io","slogan":"Wordpress is open source software you can use to create a beautiful website, blog, or app.","compose":"c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fV09SRFBSRVNTCiAgICAgIC0gV09SRFBSRVNTX0RCX0hPU1Q9bXlzcWwKICAgICAgLSBXT1JEUFJFU1NfREJfVVNFUj0kU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICAtIFdPUkRQUkVTU19EQl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9XT1JEUFJFU1MKICAgICAgLSBXT1JEUFJFU1NfREJfTkFNRT13b3JkcHJlc3MKICAgIGRlcGVuZHNfb246CiAgICAgIC0gbXlzcWwKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMScKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxMAogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo1LjcnCiAgICB2b2x1bWVzOgogICAgICAtICdteXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTVlTUUxfUk9PVF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9ST09UCiAgICAgIC0gTVlTUUxfREFUQUJBU0U9d29yZHByZXNzCiAgICAgIC0gTVlTUUxfVVNFUj0kU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICAtIE1ZU1FMX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1dPUkRQUkVTUwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIG15c3FsYWRtaW4KICAgICAgICAtIHBpbmcKICAgICAgICAtICctaCcKICAgICAgICAtIDEyNy4wLjAuMQogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==","tags":["cms","blog","content","management","mysql"],"logo":"svgs\/wordpress.svg","minversion":"0.0.0"},"wordpress-without-database":{"documentation":"https:\/\/wordpress.org?utm_source=coolify.io","slogan":"Wordpress is open source software you can use to create a beautiful website, blog, or app.","compose":"c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fV09SRFBSRVNTCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjEnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTAK","tags":["cms","blog","content","management"],"logo":"svgs\/wordpress.svg","minversion":"0.0.0"}} \ No newline at end of file From 116f5afe3c08a9b68512d44a908ee1d17db9fc56 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 25 Jun 2024 14:29:47 +0200 Subject: [PATCH 05/54] chore: Refactor ServerStatusJob constructor formatting --- app/Jobs/ServerStatusJob.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/Jobs/ServerStatusJob.php b/app/Jobs/ServerStatusJob.php index c7321a74c..f5a9659a0 100644 --- a/app/Jobs/ServerStatusJob.php +++ b/app/Jobs/ServerStatusJob.php @@ -25,7 +25,9 @@ public function backoff(): int return isDev() ? 1 : 3; } - public function __construct(public Server $server) {} + public function __construct(public Server $server) + { + } public function middleware(): array { @@ -51,7 +53,7 @@ public function handle() } } } catch (\Throwable $e) { - send_internal_notification('ServerStatusJob failed with: '.$e->getMessage()); + // send_internal_notification('ServerStatusJob failed with: '.$e->getMessage()); ray($e->getMessage()); return handleError($e); From 54c4296a25ac5e7a11a46a00ff2dfd24e56b6eb4 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 25 Jun 2024 14:29:51 +0200 Subject: [PATCH 06/54] chore: Update Monaco Editor for Docker Compose and Proxy Configuration --- .../views/livewire/project/service/edit-compose.blade.php | 3 ++- resources/views/livewire/server/proxy.blade.php | 7 ++++--- .../server/proxy/new-dynamic-configuration.blade.php | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/resources/views/livewire/project/service/edit-compose.blade.php b/resources/views/livewire/project/service/edit-compose.blade.php index ab9948cf7..2632fc0ea 100644 --- a/resources/views/livewire/project/service/edit-compose.blade.php +++ b/resources/views/livewire/project/service/edit-compose.blade.php @@ -9,7 +9,8 @@ id="service.is_container_label_escape_enabled" instantSave>
- +
diff --git a/resources/views/livewire/server/proxy.blade.php b/resources/views/livewire/server/proxy.blade.php index f20800bcd..b7050b6ac 100644 --- a/resources/views/livewire/server/proxy.blade.php +++ b/resources/views/livewire/server/proxy.blade.php @@ -18,7 +18,8 @@ Before switching proxies, please read this.
+ href="https://coolify.io/docs/knowledge-base/server/proxies#switch-between-proxies">this. + @if ($server->proxyType() === 'TRAEFIK_V2')

Traefik

@elseif ($server->proxyType() === 'CADDY') @@ -39,8 +40,8 @@
@if ($proxy_settings)
- + Reset configuration to default diff --git a/resources/views/livewire/server/proxy/new-dynamic-configuration.blade.php b/resources/views/livewire/server/proxy/new-dynamic-configuration.blade.php index fadd3f3bd..3c6bee370 100644 --- a/resources/views/livewire/server/proxy/new-dynamic-configuration.blade.php +++ b/resources/views/livewire/server/proxy/new-dynamic-configuration.blade.php @@ -1,5 +1,5 @@
- + Save From 74748963681f5599bad7762dfc664c0cad349561 Mon Sep 17 00:00:00 2001 From: andrasbacsai Date: Tue, 25 Jun 2024 12:30:37 +0000 Subject: [PATCH 07/54] Fix styling --- app/Jobs/ServerStatusJob.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/Jobs/ServerStatusJob.php b/app/Jobs/ServerStatusJob.php index f5a9659a0..bddafe2ba 100644 --- a/app/Jobs/ServerStatusJob.php +++ b/app/Jobs/ServerStatusJob.php @@ -25,9 +25,7 @@ public function backoff(): int return isDev() ? 1 : 3; } - public function __construct(public Server $server) - { - } + public function __construct(public Server $server) {} public function middleware(): array { From 41268fa20bac55eb40dfec8860fda61801b20b4e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 25 Jun 2024 15:05:51 +0200 Subject: [PATCH 08/54] api: able to update application --- app/Http/Controllers/Api/Applications.php | 62 ++++++++++++++++++----- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/app/Http/Controllers/Api/Applications.php b/app/Http/Controllers/Api/Applications.php index a638adecd..fa58e25b6 100644 --- a/app/Http/Controllers/Api/Applications.php +++ b/app/Http/Controllers/Api/Applications.php @@ -7,6 +7,7 @@ use App\Models\Application; use App\Models\Project; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Validator; use Visus\Cuid2\Cuid2; class Applications extends Controller @@ -45,6 +46,7 @@ public function application_by_uuid(Request $request) public function update_by_uuid(Request $request) { + ray()->clearAll(); $teamId = get_team_id_from_token(); if (is_null($teamId)) { return invalid_token(); @@ -63,23 +65,57 @@ public function update_by_uuid(Request $request) 'message' => 'Application not found', ], 404); } - ray($request->collect()); + $allowedFields = ['name', 'domains']; + $validator = Validator::make($request->all(), [ + 'name' => 'string|max:255', + 'domains' => 'string', + ]); + $extraFields = array_diff(array_keys($request->all()), $allowedFields); + if ($validator->fails() || ! empty($extraFields)) { + $errors = $validator->errors(); + if (! empty($extraFields)) { + foreach ($extraFields as $field) { + $errors->add($field, 'This field is not allowed.'); + } + } - // if ($request->has('domains')) { - // $existingDomains = explode(',', $application->fqdn); - // $newDomains = $request->domains; - // $filteredNewDomains = array_filter($newDomains, function ($domain) use ($existingDomains) { - // return ! in_array($domain, $existingDomains); - // }); - // $mergedDomains = array_unique(array_merge($existingDomains, $filteredNewDomains)); - // $application->fqdn = implode(',', $mergedDomains); - // $application->custom_labels = base64_encode(implode("\n ", generateLabelsApplication($application))); - // $application->save(); - // } + return response()->json([ + 'message' => 'Validation failed', + 'errors' => $errors, + ], 422); + } + + if ($request->has('domains')) { + $fqdn = $request->domains; + $fqdn = str($fqdn)->replaceEnd(',', '')->trim(); + $fqdn = str($fqdn)->replaceStart(',', '')->trim(); + $errors = []; + $fqdn = str($fqdn)->trim()->explode(',')->map(function ($domain) use (&$errors) { + if (filter_var($domain, FILTER_VALIDATE_URL) === false) { + $errors[] = 'Invalid domain: '.$domain; + + } + + return str($domain)->trim()->lower(); + }); + if (count($errors) > 0) { + return response()->json([ + 'message' => 'Validation failed', + 'errors' => $errors, + ], 422); + } + $fqdn = $fqdn->unique()->implode(','); + $application->fqdn = $fqdn; + $customLabels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n"); + $application->custom_labels = base64_encode($customLabels); + $request->offsetUnset('domains'); + } + $application->fill($request->all()); + $application->save(); return response()->json([ 'message' => 'Application updated successfully.', - 'application' => serialize_api_response($application), + 'application' => $application, ]); } From 0964c7a3387bd6790d20456bebaa2f0dd4c8f7e8 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 25 Jun 2024 21:22:14 +0200 Subject: [PATCH 09/54] remove unnecessary things from application table --- app/Livewire/Project/Application/General.php | 11 +------ app/Models/Application.php | 5 --- ..._06_25_184323_remove_docker_compose_pr.php | 32 +++++++++++++++++++ .../project/application/general.blade.php | 5 --- 4 files changed, 33 insertions(+), 20 deletions(-) create mode 100644 database/migrations/2024_06_25_184323_remove_docker_compose_pr.php diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 38eb4dce8..48be89714 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -40,8 +40,6 @@ class General extends Component public ?string $initialDockerComposeLocation = null; - public ?string $initialDockerComposePrLocation = null; - public ?Collection $parsedServices; public $parsedServiceDomains = []; @@ -72,11 +70,8 @@ class General extends Component 'application.docker_registry_image_tag' => 'nullable', 'application.dockerfile_location' => 'nullable', 'application.docker_compose_location' => 'nullable', - 'application.docker_compose_pr_location' => 'nullable', 'application.docker_compose' => 'nullable', - 'application.docker_compose_pr' => 'nullable', 'application.docker_compose_raw' => 'nullable', - 'application.docker_compose_pr_raw' => 'nullable', 'application.dockerfile_target_build' => 'nullable', 'application.docker_compose_custom_start_command' => 'nullable', 'application.docker_compose_custom_build_command' => 'nullable', @@ -114,11 +109,8 @@ class General extends Component 'application.docker_registry_image_tag' => 'Docker registry image tag', 'application.dockerfile_location' => 'Dockerfile location', 'application.docker_compose_location' => 'Docker compose location', - 'application.docker_compose_pr_location' => 'Docker compose location', 'application.docker_compose' => 'Docker compose', - 'application.docker_compose_pr' => 'Docker compose', 'application.docker_compose_raw' => 'Docker compose raw', - 'application.docker_compose_pr_raw' => 'Docker compose raw', 'application.custom_labels' => 'Custom labels', 'application.dockerfile_target_build' => 'Dockerfile target build', 'application.custom_docker_run_options' => 'Custom docker run commands', @@ -183,7 +175,7 @@ public function loadComposeFile($isInit = false) if ($isInit && $this->application->docker_compose_raw) { return; } - ['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit); + ['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation] = $this->application->loadComposeFile($isInit); if (is_null($this->parsedServices)) { $this->dispatch('error', 'Failed to parse your docker-compose file. Please check the syntax and try again.'); @@ -222,7 +214,6 @@ public function loadComposeFile($isInit = false) $this->dispatch('refreshEnvs'); } catch (\Throwable $e) { $this->application->docker_compose_location = $this->initialDockerComposeLocation; - $this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation; $this->application->save(); return handleError($e, $this); diff --git a/app/Models/Application.php b/app/Models/Application.php index 9c27fc938..98b79549f 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -964,11 +964,7 @@ public function loadComposeFile($isInit = false) ['commands' => $cloneCommand] = $this->generateGitImportCommands(deployment_uuid: $uuid, only_checkout: true, exec_in_docker: false, custom_base_dir: '.'); $workdir = rtrim($this->base_directory, '/'); $composeFile = $this->docker_compose_location; - // $prComposeFile = $this->docker_compose_pr_location; $fileList = collect([".$workdir$composeFile"]); - // if ($composeFile !== $prComposeFile) { - // $fileList->push(".$prComposeFile"); - // } $commands = collect([ "rm -rf /tmp/{$uuid}", "mkdir -p /tmp/{$uuid}", @@ -1017,7 +1013,6 @@ public function loadComposeFile($isInit = false) return [ 'parsedServices' => $parsedServices, 'initialDockerComposeLocation' => $this->docker_compose_location, - 'initialDockerComposePrLocation' => $this->docker_compose_pr_location, ]; } diff --git a/database/migrations/2024_06_25_184323_remove_docker_compose_pr.php b/database/migrations/2024_06_25_184323_remove_docker_compose_pr.php new file mode 100644 index 000000000..819a3b3f5 --- /dev/null +++ b/database/migrations/2024_06_25_184323_remove_docker_compose_pr.php @@ -0,0 +1,32 @@ +dropColumn('docker_compose_pr_location'); + $table->dropColumn('docker_compose_pr'); + $table->dropColumn('docker_compose_pr_raw'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('applications', function (Blueprint $table) { + $table->string('docker_compose_pr_location')->nullable()->default('/docker-compose.yaml')->after('docker_compose_location'); + $table->longText('docker_compose_pr')->nullable()->after('docker_compose_location'); + $table->longText('docker_compose_pr_raw')->nullable()->after('docker_compose'); + }); + } +}; diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index c19a9c7e8..42742a863 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -178,9 +178,6 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry" id="application.docker_compose_custom_start_command" helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.

So in your case, use: docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} up -d" label="Custom Start Command" /> - {{-- --}}
@else @@ -243,8 +240,6 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry" helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.

If you want to use env variables inside the labels, turn this off." id="application.settings.is_container_label_escape_enabled" instantSave> - {{-- --}} @endif @if ($application->dockerfile) From eb76d6311728a4ea960cc5f79d4ac7a49b684ba1 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 25 Jun 2024 21:22:23 +0200 Subject: [PATCH 10/54] extend application put api --- app/Http/Controllers/Api/Applications.php | 113 +++++++++++++++++++++- bootstrap/helpers/shared.php | 7 +- 2 files changed, 113 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/Api/Applications.php b/app/Http/Controllers/Api/Applications.php index fa58e25b6..080f03ed8 100644 --- a/app/Http/Controllers/Api/Applications.php +++ b/app/Http/Controllers/Api/Applications.php @@ -54,7 +54,7 @@ public function update_by_uuid(Request $request) if ($request->collect()->count() == 0) { return response()->json([ - 'message' => 'No data provided.', + 'message' => 'Invalid request.', ], 400); } $application = Application::where('uuid', $request->uuid)->first(); @@ -65,11 +65,116 @@ public function update_by_uuid(Request $request) 'message' => 'Application not found', ], 404); } - $allowedFields = ['name', 'domains']; + $server = $application->destination->server; + $allowedFields = ['name', 'description', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose', 'docker_compose_raw', 'docker_compose_domains', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'redirect']; $validator = Validator::make($request->all(), [ 'name' => 'string|max:255', + 'description' => 'string|nullable', 'domains' => 'string', + 'git_repository' => 'string', + 'git_branch' => 'string', + 'git_commit_sha' => 'string', + 'docker_registry_image_name' => 'string|nullable', + 'docker_registry_image_tag' => 'string|nullable', + 'build_pack' => 'string', + 'static_image' => 'string', + 'install_command' => 'string|nullable', + 'build_command' => 'string|nullable', + 'start_command' => 'string|nullable', + 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/', + 'ports_mappings' => 'string|regex:/^(\d+:\d+)(,\d+:\d+)*$/|nullable', + 'base_directory' => 'string|nullable', + 'publish_directory' => 'string|nullable', + 'health_check_enabled' => 'boolean', + 'health_check_path' => 'string', + 'health_check_port' => 'string|nullable', + 'health_check_host' => 'string', + 'health_check_method' => 'string', + 'health_check_return_code' => 'numeric', + 'health_check_scheme' => 'string', + 'health_check_response_text' => 'string|nullable', + 'health_check_interval' => 'numeric', + 'health_check_timeout' => 'numeric', + 'health_check_retries' => 'numeric', + 'health_check_start_period' => 'numeric', + 'limits_memory' => 'string', + 'limits_memory_swap' => 'string', + 'limits_memory_swappiness' => 'numeric', + 'limits_memory_reservation' => 'string', + 'limits_cpus' => 'string', + 'limits_cpuset' => 'string|nullable', + 'limits_cpu_shares' => 'numeric', + 'custom_labels' => 'string|nullable', + 'custom_docker_run_options' => 'string|nullable', + 'post_deployment_command' => 'string|nullable', + 'post_deployment_command_container' => 'string', + 'pre_deployment_command' => 'string|nullable', + 'pre_deployment_command_container' => 'string', + 'watch_paths' => 'string|nullable', + 'manual_webhook_secret_github' => 'string|nullable', + 'manual_webhook_secret_gitlab' => 'string|nullable', + 'manual_webhook_secret_bitbucket' => 'string|nullable', + 'manual_webhook_secret_gitea' => 'string|nullable', + 'docker_compose_location' => 'string', + 'docker_compose' => 'string|nullable', + 'docker_compose_raw' => 'string|nullable', + 'docker_compose_domains' => 'string|nullable', // must be like: "{\"api\":{\"domain\":\"http:\\/\\/b8sos8k.127.0.0.1.sslip.io\"}}" + 'docker_compose_custom_start_command' => 'string|nullable', + 'docker_compose_custom_build_command' => 'string|nullable', + 'redirect' => 'enum:both,www,non-www', ]); + + // Validate ports_exposes + if ($request->has('ports_exposes')) { + $ports = explode(',', $request->ports_exposes); + foreach ($ports as $port) { + if (! is_numeric($port)) { + return response()->json([ + 'message' => 'Validation failed', + 'errors' => [ + 'ports_exposes' => 'The ports_exposes should be a comma separated list of numbers.', + ], + ], 422); + } + } + } + // Validate ports_mappings + if ($request->has('ports_mappings')) { + $ports = []; + foreach (explode(',', $request->ports_mappings) as $portMapping) { + $port = explode(':', $portMapping); + if (in_array($port[0], $ports)) { + return response()->json([ + 'message' => 'Validation failed', + 'errors' => [ + 'ports_mappings' => 'The first number before : should be unique between mappings.', + ], + ], 422); + } + $ports[] = $port[0]; + } + } + // Validate custom_labels + if ($request->has('custom_labels')) { + if (! isBase64Encoded($request->custom_labels)) { + return response()->json([ + 'message' => 'Validation failed', + 'errors' => [ + 'custom_labels' => 'The custom_labels should be base64 encoded.', + ], + ], 422); + } + $customLabels = base64_decode($request->custom_labels); + if (mb_detect_encoding($customLabels, 'ASCII', true) === false) { + return response()->json([ + 'message' => 'Validation failed', + 'errors' => [ + 'custom_labels' => 'The custom_labels should be base64 encoded.', + ], + ], 422); + + } + } $extraFields = array_diff(array_keys($request->all()), $allowedFields); if ($validator->fails() || ! empty($extraFields)) { $errors = $validator->errors(); @@ -84,8 +189,7 @@ public function update_by_uuid(Request $request) 'errors' => $errors, ], 422); } - - if ($request->has('domains')) { + if ($request->has('domains') && $server->isProxyShouldRun()) { $fqdn = $request->domains; $fqdn = str($fqdn)->replaceEnd(',', '')->trim(); $fqdn = str($fqdn)->replaceStart(',', '')->trim(); @@ -93,7 +197,6 @@ public function update_by_uuid(Request $request) $fqdn = str($fqdn)->trim()->explode(',')->map(function ($domain) use (&$errors) { if (filter_var($domain, FILTER_VALIDATE_URL) === false) { $errors[] = 'Invalid domain: '.$domain; - } return str($domain)->trim()->lower(); diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index fe51f78ac..9d9c92330 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1907,8 +1907,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'networks' => $topLevelNetworks->toArray(), ]; if ($isSameDockerComposeFile) { - $resource->docker_compose_pr_raw = Yaml::dump($yaml, 10, 2); - $resource->docker_compose_pr = Yaml::dump($finalServices, 10, 2); $resource->docker_compose_raw = Yaml::dump($yaml, 10, 2); $resource->docker_compose = Yaml::dump($finalServices, 10, 2); } else { @@ -2316,3 +2314,8 @@ function generateSentinelToken() return $token; } + +function isBase64Encoded($strValue) +{ + return base64_encode(base64_decode($strValue, true)) === $strValue; +} From f45b3cab55832487dbc293761b9965ab5c388262 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 26 Jun 2024 13:00:36 +0200 Subject: [PATCH 11/54] feat: more API endpoints --- app/Enums/RedirectTypes.php | 10 + app/Http/Controllers/Api/Applications.php | 333 +++++++++++++++++- app/Http/Controllers/Api/Deploy.php | 21 +- app/Http/Controllers/Api/Domains.php | 54 --- .../Controllers/Api/EnvironmentVariables.php | 46 +++ app/Http/Controllers/Api/Server.php | 2 +- app/Models/Application.php | 5 + app/Models/EnvironmentVariable.php | 8 +- ...pr.php => 2024_06_25_184323_update_db.php} | 18 + routes/api.php | 34 +- 10 files changed, 433 insertions(+), 98 deletions(-) create mode 100644 app/Enums/RedirectTypes.php delete mode 100644 app/Http/Controllers/Api/Domains.php create mode 100644 app/Http/Controllers/Api/EnvironmentVariables.php rename database/migrations/{2024_06_25_184323_remove_docker_compose_pr.php => 2024_06_25_184323_update_db.php} (58%) diff --git a/app/Enums/RedirectTypes.php b/app/Enums/RedirectTypes.php new file mode 100644 index 000000000..efe8bd9de --- /dev/null +++ b/app/Enums/RedirectTypes.php @@ -0,0 +1,10 @@ +push($projects->pluck('applications')->flatten()); $applications = $applications->flatten(); - return response()->json($applications); + return response()->json(serialize_api_response($applications)); } public function application_by_uuid(Request $request) @@ -36,12 +40,42 @@ public function application_by_uuid(Request $request) if (! $uuid) { return response()->json(['error' => 'UUID is required.'], 400); } - $application = Application::where('uuid', $uuid)->first(); + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); if (! $application) { return response()->json(['error' => 'Application not found.'], 404); } - return response()->json($application); + return response()->json(serialize_api_response($application)); + } + + public function delete_by_uuid(Request $request) + { + ray()->clearAll(); + $teamId = get_team_id_from_token(); + $cleanup = $request->query->get('cleanup') ?? false; + if (is_null($teamId)) { + return invalid_token(); + } + + if ($request->collect()->count() == 0) { + return response()->json([ + 'message' => 'Invalid request.', + ], 400); + } + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); + + if (! $application) { + return response()->json([ + 'success' => false, + 'message' => 'Application not found', + ], 404); + } + DeleteResourceJob::dispatch($application, $cleanup); + + return response()->json([ + 'success' => true, + 'message' => 'Application deletion request queued.', + ]); } public function update_by_uuid(Request $request) @@ -57,7 +91,7 @@ public function update_by_uuid(Request $request) 'message' => 'Invalid request.', ], 400); } - $application = Application::where('uuid', $request->uuid)->first(); + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); if (! $application) { return response()->json([ @@ -66,7 +100,7 @@ public function update_by_uuid(Request $request) ], 404); } $server = $application->destination->server; - $allowedFields = ['name', 'description', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose', 'docker_compose_raw', 'docker_compose_domains', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'redirect']; + $allowedFields = ['name', 'description', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'redirect']; $validator = Validator::make($request->all(), [ 'name' => 'string|max:255', 'description' => 'string|nullable', @@ -118,10 +152,10 @@ public function update_by_uuid(Request $request) 'docker_compose_location' => 'string', 'docker_compose' => 'string|nullable', 'docker_compose_raw' => 'string|nullable', - 'docker_compose_domains' => 'string|nullable', // must be like: "{\"api\":{\"domain\":\"http:\\/\\/b8sos8k.127.0.0.1.sslip.io\"}}" + // 'docker_compose_domains' => 'string|nullable', // must be like: "{\"api\":{\"domain\":\"http:\\/\\/b8sos8k.127.0.0.1.sslip.io\"}}" 'docker_compose_custom_start_command' => 'string|nullable', 'docker_compose_custom_build_command' => 'string|nullable', - 'redirect' => 'enum:both,www,non-www', + 'redirect' => Rule::enum(RedirectTypes::class), ]); // Validate ports_exposes @@ -130,7 +164,7 @@ public function update_by_uuid(Request $request) foreach ($ports as $port) { if (! is_numeric($port)) { return response()->json([ - 'message' => 'Validation failed', + 'message' => 'Validation failed.', 'errors' => [ 'ports_exposes' => 'The ports_exposes should be a comma separated list of numbers.', ], @@ -145,7 +179,7 @@ public function update_by_uuid(Request $request) $port = explode(':', $portMapping); if (in_array($port[0], $ports)) { return response()->json([ - 'message' => 'Validation failed', + 'message' => 'Validation failed.', 'errors' => [ 'ports_mappings' => 'The first number before : should be unique between mappings.', ], @@ -158,7 +192,7 @@ public function update_by_uuid(Request $request) if ($request->has('custom_labels')) { if (! isBase64Encoded($request->custom_labels)) { return response()->json([ - 'message' => 'Validation failed', + 'message' => 'Validation failed.', 'errors' => [ 'custom_labels' => 'The custom_labels should be base64 encoded.', ], @@ -167,7 +201,7 @@ public function update_by_uuid(Request $request) $customLabels = base64_decode($request->custom_labels); if (mb_detect_encoding($customLabels, 'ASCII', true) === false) { return response()->json([ - 'message' => 'Validation failed', + 'message' => 'Validation failed.', 'errors' => [ 'custom_labels' => 'The custom_labels should be base64 encoded.', ], @@ -185,7 +219,7 @@ public function update_by_uuid(Request $request) } return response()->json([ - 'message' => 'Validation failed', + 'message' => 'Validation failed.', 'errors' => $errors, ], 422); } @@ -203,7 +237,7 @@ public function update_by_uuid(Request $request) }); if (count($errors) > 0) { return response()->json([ - 'message' => 'Validation failed', + 'message' => 'Validation failed.', 'errors' => $errors, ], 422); } @@ -216,9 +250,272 @@ public function update_by_uuid(Request $request) $application->fill($request->all()); $application->save(); + return response()->json(serialize_api_response($application)); + } + + public function envs_by_uuid(Request $request) + { + ray()->clearAll(); + $teamId = get_team_id_from_token(); + if (is_null($teamId)) { + return invalid_token(); + } + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); + + if (! $application) { + return response()->json([ + 'success' => false, + 'message' => 'Application not found', + ], 404); + } + $envs = $application->environment_variables->sortBy('id')->merge($application->environment_variables_preview->sortBy('id')); + + return response()->json(serialize_api_response($envs)); + } + + public function update_env_by_uuid(Request $request) + { + ray()->clearAll(); + $allowedFields = ['key', 'value', 'is_preview', 'is_build_time', 'is_literal', 'both']; + $teamId = get_team_id_from_token(); + + if (is_null($teamId)) { + return invalid_token(); + } + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); + + if (! $application) { + return response()->json([ + 'success' => false, + 'message' => 'Application not found', + ], 404); + } + $validator = Validator::make($request->all(), [ + 'key' => 'string|required', + 'value' => 'string|nullable', + 'is_preview' => 'boolean', + 'is_build_time' => 'boolean', + 'is_literal' => 'boolean', + 'both' => 'boolean', + ]); + + $extraFields = array_diff(array_keys($request->all()), $allowedFields); + if ($validator->fails() || ! empty($extraFields)) { + $errors = $validator->errors(); + if (! empty($extraFields)) { + foreach ($extraFields as $field) { + $errors->add($field, 'This field is not allowed.'); + } + } + + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + $is_preview = $request->is_preview ?? false; + $is_build_time = $request->is_build_time ?? false; + $is_literal = $request->is_literal ?? false; + $both = $request->both ?? false; + if ($both) { + $env = $application->environment_variables_preview->where('key', $request->key)->first(); + if ($env) { + $env->value = $request->value; + if ($env->is_build_time != $is_build_time) { + $env->is_build_time = $is_build_time; + } + if ($env->is_literal != $is_literal) { + $env->is_literal = $is_literal; + } + ray($env); + $env->save(); + } + + $env = $application->environment_variables->where('key', $request->key)->first(); + if ($env) { + $env->value = $request->value; + if ($env->is_build_time != $is_build_time) { + $env->is_build_time = $is_build_time; + } + if ($env->is_literal != $is_literal) { + $env->is_literal = $is_literal; + } + $env->save(); + } + + return response()->json([ + 'message' => 'Environment variables updated.', + ]); + } + if ($is_preview) { + $env = $application->environment_variables_preview->where('key', $request->key)->first(); + if ($env) { + $env->value = $request->value; + if ($env->is_build_time != $is_build_time) { + $env->is_build_time = $is_build_time; + } + if ($env->is_literal != $is_literal) { + $env->is_literal = $is_literal; + } + if ($env->is_preview != $is_preview) { + $env->is_preview = $is_preview; + } + $env->save(); + + return response()->json(serialize_api_response($env)); + } else { + return response()->json([ + 'message' => 'Environment variable not found.', + ], 404); + } + } else { + $env = $application->environment_variables->where('key', $request->key)->first(); + if ($env) { + $env->value = $request->value; + if ($env->is_build_time != $is_build_time) { + $env->is_build_time = $is_build_time; + } + if ($env->is_literal != $is_literal) { + $env->is_literal = $is_literal; + } + if ($env->is_preview != $is_preview) { + $env->is_preview = $is_preview; + } + $env->save(); + + return response()->json(serialize_api_response($env)); + } else { + + return response()->json([ + 'message' => 'Environment variable not found.', + ], 404); + + } + } + return response()->json([ - 'message' => 'Application updated successfully.', - 'application' => $application, + 'message' => 'Something went wrong.', + ], 500); + + } + + public function create_env(Request $request) + { + ray()->clearAll(); + $allowedFields = ['key', 'value', 'is_preview', 'is_build_time', 'is_literal']; + $teamId = get_team_id_from_token(); + + if (is_null($teamId)) { + return invalid_token(); + } + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); + + if (! $application) { + return response()->json([ + 'success' => false, + 'message' => 'Application not found', + ], 404); + } + $validator = Validator::make($request->all(), [ + 'key' => 'string|required', + 'value' => 'string|nullable', + 'is_preview' => 'boolean', + 'is_build_time' => 'boolean', + 'is_literal' => 'boolean', + ]); + + $extraFields = array_diff(array_keys($request->all()), $allowedFields); + if ($validator->fails() || ! empty($extraFields)) { + $errors = $validator->errors(); + if (! empty($extraFields)) { + foreach ($extraFields as $field) { + $errors->add($field, 'This field is not allowed.'); + } + } + + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + $is_preview = $request->is_preview ?? false; + if ($is_preview) { + $env = $application->environment_variables_preview->where('key', $request->key)->first(); + if ($env) { + return response()->json([ + 'message' => 'Environment variable already exists. Use PATCH request to update it.', + ], 409); + } else { + $env = $application->environment_variables()->create([ + 'key' => $request->key, + 'value' => $request->value, + 'is_preview' => $request->is_preview ?? false, + 'is_build_time' => $request->is_build_time ?? false, + 'is_literal' => $request->is_literal ?? false, + ]); + + return response()->json(serialize_api_response($env))->setStatusCode(201); + } + } else { + $env = $application->environment_variables->where('key', $request->key)->first(); + if ($env) { + return response()->json([ + 'message' => 'Environment variable already exists. Use PATCH request to update it.', + ], 409); + } else { + $env = $application->environment_variables()->create([ + 'key' => $request->key, + 'value' => $request->value, + 'is_preview' => $request->is_preview ?? false, + 'is_build_time' => $request->is_build_time ?? false, + 'is_literal' => $request->is_literal ?? false, + ]); + + return response()->json(serialize_api_response($env))->setStatusCode(201); + + } + } + + return response()->json([ + 'message' => 'Something went wrong.', + ], 500); + + } + + public function delete_env_by_uuid(Request $request) + { + ray()->clearAll(); + $teamId = get_team_id_from_token(); + $both = $request->query->get('both') ?? false; + if (is_null($teamId)) { + return invalid_token(); + } + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); + + if (! $application) { + return response()->json([ + 'success' => false, + 'message' => 'Application not found.', + ], 404); + } + $found_env = EnvironmentVariable::where('uuid', $request->env_uuid)->where('application_id', $application->id)->first(); + if (! $found_env) { + return response()->json([ + 'success' => false, + 'message' => 'Environment variable not found.', + ], 404); + } + $found_env->delete(); + if ($both) { + $found_other_pair = EnvironmentVariable::where('application_id', $application->id)->where('key', $found_env->key)->first(); + if ($found_other_pair) { + $found_other_pair->delete(); + } + } + + return response()->json([ + 'success' => true, + 'message' => 'Environment variable deleted.', ]); } @@ -234,7 +531,7 @@ public function action_deploy(Request $request) if (! $uuid) { return response()->json(['error' => 'UUID is required.'], 400); } - $application = Application::where('uuid', $uuid)->first(); + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); if (! $application) { return response()->json(['error' => 'Application not found.'], 404); } @@ -270,7 +567,7 @@ public function action_stop(Request $request) if (! $uuid) { return response()->json(['error' => 'UUID is required.'], 400); } - $application = Application::where('uuid', $uuid)->first(); + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); if (! $application) { return response()->json(['error' => 'Application not found.'], 404); } @@ -295,7 +592,7 @@ public function action_restart(Request $request) if (! $uuid) { return response()->json(['error' => 'UUID is required.'], 400); } - $application = Application::where('uuid', $uuid)->first(); + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); if (! $application) { return response()->json(['error' => 'Application not found.'], 404); } diff --git a/app/Http/Controllers/Api/Deploy.php b/app/Http/Controllers/Api/Deploy.php index f7a34facc..d510970dd 100644 --- a/app/Http/Controllers/Api/Deploy.php +++ b/app/Http/Controllers/Api/Deploy.php @@ -49,14 +49,14 @@ public function deployment_by_uuid(Request $request) } $uuid = $request->route('uuid'); if (! $uuid) { - return response()->json(['error' => 'UUID is required.'], 400); + return response()->json(['message' => 'UUID is required.'], 400); } - $deployment = ApplicationDeploymentQueue::where('deployment_uuid', $uuid)->first()->makeHidden('logs'); + $deployment = ApplicationDeploymentQueue::where('deployment_uuid', $uuid)->first(); if (! $deployment) { - return response()->json(['error' => 'Deployment not found.'], 404); + return response()->json(['message' => 'Deployment not found.'], 404); } - return response()->json(serialize_api_response($deployment), 200); + return response()->json(serialize_api_response($deployment->makeHidden('logs')), 200); } public function deploy(Request $request) @@ -67,7 +67,7 @@ public function deploy(Request $request) $force = $request->query->get('force') ?? false; if ($uuids && $tags) { - return response()->json(['error' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); + return response()->json(['message' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); } if (is_null($teamId)) { return invalid_token(); @@ -78,7 +78,7 @@ public function deploy(Request $request) return $this->by_uuids($uuids, $teamId, $force); } - return response()->json(['error' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); + return response()->json(['message' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); } private function by_uuids(string $uuid, int $teamId, bool $force = false) @@ -87,7 +87,7 @@ private function by_uuids(string $uuid, int $teamId, bool $force = false) $uuids = collect(array_filter($uuids)); if (count($uuids) === 0) { - return response()->json(['error' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); + return response()->json(['message' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); } $deployments = collect(); $payload = collect(); @@ -108,7 +108,7 @@ private function by_uuids(string $uuid, int $teamId, bool $force = false) return response()->json($payload->toArray(), 200); } - return response()->json(['error' => 'No resources found.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404); + return response()->json(['message' => 'No resources found.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404); } public function by_tags(string $tags, int $team_id, bool $force = false) @@ -117,7 +117,7 @@ public function by_tags(string $tags, int $team_id, bool $force = false) $tags = collect(array_filter($tags)); if (count($tags) === 0) { - return response()->json(['error' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); + return response()->json(['message' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); } $message = collect([]); $deployments = collect(); @@ -147,7 +147,6 @@ public function by_tags(string $tags, int $team_id, bool $force = false) $message = $message->merge($return_message); } } - ray($message); if ($message->count() > 0) { $payload->put('message', $message->toArray()); if ($deployments->count() > 0) { @@ -157,7 +156,7 @@ public function by_tags(string $tags, int $team_id, bool $force = false) return response()->json($payload->toArray(), 200); } - return response()->json(['error' => 'No resources found with this tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404); + return response()->json(['message' => 'No resources found with this tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404); } public function deploy_resource($resource, bool $force = false): array diff --git a/app/Http/Controllers/Api/Domains.php b/app/Http/Controllers/Api/Domains.php deleted file mode 100644 index 6473b64de..000000000 --- a/app/Http/Controllers/Api/Domains.php +++ /dev/null @@ -1,54 +0,0 @@ -all(), [ - 'uuid' => 'required|string|exists:applications,uuid', - 'domains' => 'required|array', - 'domains.*' => 'required|string|distinct', - ]); - - if ($validator->fails()) { - return response()->json([ - 'success' => false, - 'message' => 'Validation failed', - 'errors' => $validator->errors(), - ], 422); - } - - $application = Application::where('uuid', $request->uuid)->first(); - - if (! $application) { - return response()->json([ - 'success' => false, - 'message' => 'Application not found', - ], 404); - } - - $existingDomains = explode(',', $application->fqdn); - $domainsToDelete = $request->domains; - $updatedDomains = array_diff($existingDomains, $domainsToDelete); - $application->fqdn = implode(',', $updatedDomains); - $application->custom_labels = base64_encode(implode("\n ", generateLabelsApplication($application))); - $application->save(); - - return response()->json([ - 'success' => true, - 'message' => 'Domains updated successfully', - 'application' => $application, - ]); - } -} diff --git a/app/Http/Controllers/Api/EnvironmentVariables.php b/app/Http/Controllers/Api/EnvironmentVariables.php new file mode 100644 index 000000000..d27d4f501 --- /dev/null +++ b/app/Http/Controllers/Api/EnvironmentVariables.php @@ -0,0 +1,46 @@ +clearAll(); + $teamId = get_team_id_from_token(); + $both = $request->query->get('both') ?? false; + if (is_null($teamId)) { + return invalid_token(); + } + $env = EnvironmentVariable::where('uuid', $request->env_uuid)->first(); + if (! $env) { + return response()->json([ + 'success' => false, + 'message' => 'Environment variable not found.', + ], 404); + } + $found_app = $env->resource()->whereRelation('environment.project.team', 'id', $teamId)->first(); + if (! $found_app) { + return response()->json([ + 'success' => false, + 'message' => 'Environment variable not found.', + ], 404); + } + $env->delete(); + if ($both) { + $found_other_pair = EnvironmentVariable::where('application_id', $found_app->id)->where('key', $env->key)->first(); + if ($found_other_pair) { + $found_other_pair->delete(); + } + } + + return response()->json([ + 'success' => true, + 'message' => 'Environment variable deleted.', + ]); + } +} diff --git a/app/Http/Controllers/Api/Server.php b/app/Http/Controllers/Api/Server.php index 7a6090bf8..1a58da7b0 100644 --- a/app/Http/Controllers/Api/Server.php +++ b/app/Http/Controllers/Api/Server.php @@ -36,7 +36,7 @@ public function server_by_uuid(Request $request) } $server = ModelsServer::whereTeamId($teamId)->whereUuid(request()->uuid)->first(); if (is_null($server)) { - return response()->json(['error' => 'Server not found.'], 404); + return response()->json(['message' => 'Server not found.'], 404); } if ($with_resources) { $server['resources'] = $server->definedResources()->map(function ($resource) { diff --git a/app/Models/Application.php b/app/Models/Application.php index 98b79549f..8b9ff8ee7 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -60,6 +60,11 @@ protected static function booted() }); } + public static function ownedByCurrentTeamAPI(int $teamId) + { + return Application::whereRelation('environment.project.team', 'id', $teamId)->orderBy('name'); + } + public function delete_configurations() { $server = data_get($this, 'destination.server'); diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php index 8731eab2d..04a556274 100644 --- a/app/Models/EnvironmentVariable.php +++ b/app/Models/EnvironmentVariable.php @@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Str; use Symfony\Component\Yaml\Yaml; +use Visus\Cuid2\Cuid2; class EnvironmentVariable extends Model { @@ -25,6 +26,11 @@ class EnvironmentVariable extends Model protected static function booted() { + static::creating(function (Model $model) { + if (! $model->uuid) { + $model->uuid = (string) new Cuid2(); + } + }); static::created(function (EnvironmentVariable $environment_variable) { if ($environment_variable->application_id && ! $environment_variable->is_preview) { $found = ModelsEnvironmentVariable::where('key', $environment_variable->key)->where('application_id', $environment_variable->application_id)->where('is_preview', true)->first(); @@ -220,7 +226,7 @@ private function set_environment_variables(?string $environment_variable = null) protected function key(): Attribute { return Attribute::make( - set: fn (string $value) => str($value)->trim(), + set: fn (string $value) => str($value)->trim()->replace(' ', '_')->value, ); } } diff --git a/database/migrations/2024_06_25_184323_remove_docker_compose_pr.php b/database/migrations/2024_06_25_184323_update_db.php similarity index 58% rename from database/migrations/2024_06_25_184323_remove_docker_compose_pr.php rename to database/migrations/2024_06_25_184323_update_db.php index 819a3b3f5..ef6ee74f7 100644 --- a/database/migrations/2024_06_25_184323_remove_docker_compose_pr.php +++ b/database/migrations/2024_06_25_184323_update_db.php @@ -1,8 +1,10 @@ dropColumn('docker_compose_pr'); $table->dropColumn('docker_compose_pr_raw'); }); + Schema::table('environment_variables', function (Blueprint $table) { + $table->string('uuid')->nullable()->after('id'); + }); + + EnvironmentVariable::all()->each(function (EnvironmentVariable $environmentVariable) { + $environmentVariable->update([ + 'uuid' => (string) new Cuid2(), + ]); + }); + Schema::table('environment_variables', function (Blueprint $table) { + $table->string('uuid')->nullable(false)->change(); + }); } /** @@ -28,5 +42,9 @@ public function down(): void $table->longText('docker_compose_pr')->nullable()->after('docker_compose_location'); $table->longText('docker_compose_pr_raw')->nullable()->after('docker_compose'); }); + Schema::table('environment_variables', function (Blueprint $table) { + $table->dropColumn('uuid'); + }); + } }; diff --git a/routes/api.php b/routes/api.php index 858a2282f..41a369f3d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -2,7 +2,7 @@ use App\Http\Controllers\Api\Applications; use App\Http\Controllers\Api\Deploy; -use App\Http\Controllers\Api\Domains; +use App\Http\Controllers\Api\EnvironmentVariables; use App\Http\Controllers\Api\Resources; use App\Http\Controllers\Api\Server; use App\Http\Controllers\Api\Team; @@ -34,28 +34,36 @@ }); Route::match(['get', 'post'], '/deploy', [Deploy::class, 'deploy']); Route::get('/deployments', [Deploy::class, 'deployments']); - Route::get('/deployment/{uuid}', [Deploy::class, 'deployment_by_uuid']); + Route::get('/deployments/{uuid}', [Deploy::class, 'deployment_by_uuid']); Route::get('/servers', [Server::class, 'servers']); - Route::get('/server/{uuid}', [Server::class, 'server_by_uuid']); + Route::get('/servers/{uuid}', [Server::class, 'server_by_uuid']); Route::get('/servers/domains', [Server::class, 'get_domains_by_server']); Route::get('/resources', [Resources::class, 'resources']); Route::get('/applications', [Applications::class, 'applications']); - Route::get('/application/{uuid}', [Applications::class, 'application_by_uuid']); - Route::put('/application/{uuid}', [Applications::class, 'update_by_uuid']); - Route::match(['get', 'post'], '/application/{uuid}/action/deploy', [Applications::class, 'action_deploy']); - Route::match(['get', 'post'], '/application/{uuid}/action/restart', [Applications::class, 'action_restart']); - Route::match(['get', 'post'], '/application/{uuid}/action/stop', [Applications::class, 'action_stop']); - Route::delete('/domains', [Domains::class, 'deleteDomains']); + Route::get('/applications/{uuid}', [Applications::class, 'application_by_uuid']); + Route::patch('/applications/{uuid}', [Applications::class, 'update_by_uuid']); + Route::delete('/applications/{uuid}', [Applications::class, 'delete_by_uuid']); + + Route::get('/applications/{uuid}/envs', [Applications::class, 'envs_by_uuid']); + Route::post('/applications/{uuid}/envs', [Applications::class, 'create_env']); + Route::patch('/applications/{uuid}/envs', [Applications::class, 'update_env_by_uuid']); + Route::delete('/applications/{uuid}/envs/{env_uuid}', [Applications::class, 'delete_env_by_uuid']); + + Route::delete('/envs/{env_uuid}', [EnvironmentVariables::class, 'delete_env_by_uuid']); + + Route::match(['get', 'post'], '/applications/{uuid}/action/deploy', [Applications::class, 'action_deploy']); + Route::match(['get', 'post'], '/applications/{uuid}/action/restart', [Applications::class, 'action_restart']); + Route::match(['get', 'post'], '/applications/{uuid}/action/stop', [Applications::class, 'action_stop']); Route::get('/teams', [Team::class, 'teams']); - Route::get('/team/current', [Team::class, 'current_team']); - Route::get('/team/current/members', [Team::class, 'current_team_members']); - Route::get('/team/{id}', [Team::class, 'team_by_id']); - Route::get('/team/{id}/members', [Team::class, 'members_by_id']); + Route::get('/teams/current', [Team::class, 'current_team']); + Route::get('/teams/current/members', [Team::class, 'current_team_members']); + Route::get('/teams/{id}', [Team::class, 'team_by_id']); + Route::get('/teams/{id}/members', [Team::class, 'members_by_id']); // Route::get('/projects', [Project::class, 'projects']); //Route::get('/project/{uuid}', [Project::class, 'project_by_uuid']); From 2a52fb5872d9321dc89259f83de4af5da8425e8e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 26 Jun 2024 13:32:36 +0200 Subject: [PATCH 12/54] feat: bulk env update api endpoint --- app/Http/Controllers/Api/Applications.php | 144 +++++++++++++++++++++- bootstrap/helpers/shared.php | 11 ++ routes/api.php | 1 + 3 files changed, 152 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Api/Applications.php b/app/Http/Controllers/Api/Applications.php index d19e73a1b..8bbb14a07 100644 --- a/app/Http/Controllers/Api/Applications.php +++ b/app/Http/Controllers/Api/Applications.php @@ -10,7 +10,6 @@ use App\Models\EnvironmentVariable; use App\Models\Project; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; use Visus\Cuid2\Cuid2; @@ -101,7 +100,8 @@ public function update_by_uuid(Request $request) } $server = $application->destination->server; $allowedFields = ['name', 'description', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'redirect']; - $validator = Validator::make($request->all(), [ + + $validator = customApiValidator($request->all(), [ 'name' => 'string|max:255', 'description' => 'string|nullable', 'domains' => 'string', @@ -290,7 +290,7 @@ public function update_env_by_uuid(Request $request) 'message' => 'Application not found', ], 404); } - $validator = Validator::make($request->all(), [ + $validator = customApiValidator($request->all(), [ 'key' => 'string|required', 'value' => 'string|nullable', 'is_preview' => 'boolean', @@ -399,6 +399,142 @@ public function update_env_by_uuid(Request $request) } + public function create_bulk_envs(Request $request) + { + ray()->clearAll(); + $teamId = get_team_id_from_token(); + + if (is_null($teamId)) { + return invalid_token(); + } + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); + + if (! $application) { + return response()->json([ + 'success' => false, + 'message' => 'Application not found', + ], 404); + } + + $bulk_data = $request->get('data'); + if (! $bulk_data) { + return response()->json([ + 'message' => 'Bulk data is required.', + ], 400); + } + $bulk_data = collect($bulk_data)->map(function ($item) { + return collect($item)->only(['key', 'value', 'is_preview', 'is_build_time', 'is_literal', 'both']); + }); + foreach ($bulk_data as $item) { + $validator = customApiValidator($item, [ + 'key' => 'string|required', + 'value' => 'string|nullable', + 'is_preview' => 'boolean', + 'is_build_time' => 'boolean', + 'is_literal' => 'boolean', + 'both' => 'boolean', + ]); + if ($validator->fails()) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $validator->errors(), + ], 422); + } + $is_preview = $item->get('is_preview') ?? false; + $is_build_time = $item->get('is_build_time') ?? false; + $is_literal = $item->get('is_literal') ?? false; + $both = $item->get('both') ?? false; + if ($both) { + $env = $application->environment_variables_preview->where('key', $item->get('key'))->first(); + if ($env) { + $env->value = $item->get('value'); + if ($env->is_build_time != $is_build_time) { + $env->is_build_time = $is_build_time; + } + if ($env->is_literal != $is_literal) { + $env->is_literal = $is_literal; + } + $env->save(); + } else { + $env = $application->environment_variables()->create([ + 'key' => $item->get('key'), + 'value' => $item->get('value'), + 'is_preview' => $is_preview, + 'is_build_time' => $is_build_time, + 'is_literal' => $is_literal, + ]); + } + + $env = $application->environment_variables->where('key', $item->get('key'))->first(); + if ($env) { + $env->value = $item->get('value'); + if ($env->is_build_time != $is_build_time) { + $env->is_build_time = $is_build_time; + } + if ($env->is_literal != $is_literal) { + $env->is_literal = $is_literal; + } + $env->save(); + } else { + $env = $application->environment_variables()->create([ + 'key' => $item->get('key'), + 'value' => $item->get('value'), + 'is_preview' => $is_preview, + 'is_build_time' => $is_build_time, + 'is_literal' => $is_literal, + ]); + } + + continue; + } + if ($is_preview) { + $env = $application->environment_variables_preview->where('key', $item->get('key'))->first(); + if ($env) { + $env->value = $item->get('value'); + if ($env->is_build_time != $is_build_time) { + $env->is_build_time = $is_build_time; + } + if ($env->is_literal != $is_literal) { + $env->is_literal = $is_literal; + } + $env->save(); + } else { + $env = $application->environment_variables()->create([ + 'key' => $item->get('key'), + 'value' => $item->get('value'), + 'is_preview' => $is_preview, + 'is_build_time' => $is_build_time, + 'is_literal' => $is_literal, + ]); + } + } else { + $env = $application->environment_variables->where('key', $item->get('key'))->first(); + if ($env) { + $env->value = $item->get('value'); + if ($env->is_build_time != $is_build_time) { + $env->is_build_time = $is_build_time; + } + if ($env->is_literal != $is_literal) { + $env->is_literal = $is_literal; + } + $env->save(); + } else { + $env = $application->environment_variables()->create([ + 'key' => $item->get('key'), + 'value' => $item->get('value'), + 'is_preview' => $is_preview, + 'is_build_time' => $is_build_time, + 'is_literal' => $is_literal, + ]); + } + } + } + + return response()->json([ + 'message' => 'Environments updated.', + ]); + } + public function create_env(Request $request) { ray()->clearAll(); @@ -416,7 +552,7 @@ public function create_env(Request $request) 'message' => 'Application not found', ], 404); } - $validator = Validator::make($request->all(), [ + $validator = customApiValidator($request->all(), [ 'key' => 'string|required', 'value' => 'string|nullable', 'is_preview' => 'boolean', diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 9d9c92330..a4676cfd4 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -40,6 +40,7 @@ use Illuminate\Support\Facades\Process; use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\Route; +use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; use Illuminate\Support\Stringable; use Lcobucci\JWT\Encoding\ChainedFormatter; @@ -2319,3 +2320,13 @@ function isBase64Encoded($strValue) { return base64_encode(base64_decode($strValue, true)) === $strValue; } +function customApiValidator(Collection|array $item, array $rules) +{ + if (is_array($item)) { + $item = collect($item); + } + + return Validator::make($item->toArray(), $rules, [ + 'required' => 'This field is required.', + ]); +} diff --git a/routes/api.php b/routes/api.php index 41a369f3d..7aca146ba 100644 --- a/routes/api.php +++ b/routes/api.php @@ -50,6 +50,7 @@ Route::get('/applications/{uuid}/envs', [Applications::class, 'envs_by_uuid']); Route::post('/applications/{uuid}/envs', [Applications::class, 'create_env']); + Route::post('/applications/{uuid}/envs/bulk', [Applications::class, 'create_bulk_envs']); Route::patch('/applications/{uuid}/envs', [Applications::class, 'update_env_by_uuid']); Route::delete('/applications/{uuid}/envs/{env_uuid}', [Applications::class, 'delete_env_by_uuid']); From 07508df8fd9808047361a86eb541f8b0261595da Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 26 Jun 2024 13:57:04 +0200 Subject: [PATCH 13/54] fix: remove both option for api endpoints. it just makes things complicated --- app/Http/Controllers/Api/Applications.php | 88 +------------------ .../Controllers/Api/EnvironmentVariables.php | 7 -- 2 files changed, 2 insertions(+), 93 deletions(-) diff --git a/app/Http/Controllers/Api/Applications.php b/app/Http/Controllers/Api/Applications.php index 8bbb14a07..82fde140c 100644 --- a/app/Http/Controllers/Api/Applications.php +++ b/app/Http/Controllers/Api/Applications.php @@ -276,7 +276,7 @@ public function envs_by_uuid(Request $request) public function update_env_by_uuid(Request $request) { ray()->clearAll(); - $allowedFields = ['key', 'value', 'is_preview', 'is_build_time', 'is_literal', 'both']; + $allowedFields = ['key', 'value', 'is_preview', 'is_build_time', 'is_literal']; $teamId = get_team_id_from_token(); if (is_null($teamId)) { @@ -296,7 +296,6 @@ public function update_env_by_uuid(Request $request) 'is_preview' => 'boolean', 'is_build_time' => 'boolean', 'is_literal' => 'boolean', - 'both' => 'boolean', ]); $extraFields = array_diff(array_keys($request->all()), $allowedFields); @@ -316,37 +315,6 @@ public function update_env_by_uuid(Request $request) $is_preview = $request->is_preview ?? false; $is_build_time = $request->is_build_time ?? false; $is_literal = $request->is_literal ?? false; - $both = $request->both ?? false; - if ($both) { - $env = $application->environment_variables_preview->where('key', $request->key)->first(); - if ($env) { - $env->value = $request->value; - if ($env->is_build_time != $is_build_time) { - $env->is_build_time = $is_build_time; - } - if ($env->is_literal != $is_literal) { - $env->is_literal = $is_literal; - } - ray($env); - $env->save(); - } - - $env = $application->environment_variables->where('key', $request->key)->first(); - if ($env) { - $env->value = $request->value; - if ($env->is_build_time != $is_build_time) { - $env->is_build_time = $is_build_time; - } - if ($env->is_literal != $is_literal) { - $env->is_literal = $is_literal; - } - $env->save(); - } - - return response()->json([ - 'message' => 'Environment variables updated.', - ]); - } if ($is_preview) { $env = $application->environment_variables_preview->where('key', $request->key)->first(); if ($env) { @@ -423,7 +391,7 @@ public function create_bulk_envs(Request $request) ], 400); } $bulk_data = collect($bulk_data)->map(function ($item) { - return collect($item)->only(['key', 'value', 'is_preview', 'is_build_time', 'is_literal', 'both']); + return collect($item)->only(['key', 'value', 'is_preview', 'is_build_time', 'is_literal']); }); foreach ($bulk_data as $item) { $validator = customApiValidator($item, [ @@ -432,7 +400,6 @@ public function create_bulk_envs(Request $request) 'is_preview' => 'boolean', 'is_build_time' => 'boolean', 'is_literal' => 'boolean', - 'both' => 'boolean', ]); if ($validator->fails()) { return response()->json([ @@ -443,50 +410,6 @@ public function create_bulk_envs(Request $request) $is_preview = $item->get('is_preview') ?? false; $is_build_time = $item->get('is_build_time') ?? false; $is_literal = $item->get('is_literal') ?? false; - $both = $item->get('both') ?? false; - if ($both) { - $env = $application->environment_variables_preview->where('key', $item->get('key'))->first(); - if ($env) { - $env->value = $item->get('value'); - if ($env->is_build_time != $is_build_time) { - $env->is_build_time = $is_build_time; - } - if ($env->is_literal != $is_literal) { - $env->is_literal = $is_literal; - } - $env->save(); - } else { - $env = $application->environment_variables()->create([ - 'key' => $item->get('key'), - 'value' => $item->get('value'), - 'is_preview' => $is_preview, - 'is_build_time' => $is_build_time, - 'is_literal' => $is_literal, - ]); - } - - $env = $application->environment_variables->where('key', $item->get('key'))->first(); - if ($env) { - $env->value = $item->get('value'); - if ($env->is_build_time != $is_build_time) { - $env->is_build_time = $is_build_time; - } - if ($env->is_literal != $is_literal) { - $env->is_literal = $is_literal; - } - $env->save(); - } else { - $env = $application->environment_variables()->create([ - 'key' => $item->get('key'), - 'value' => $item->get('value'), - 'is_preview' => $is_preview, - 'is_build_time' => $is_build_time, - 'is_literal' => $is_literal, - ]); - } - - continue; - } if ($is_preview) { $env = $application->environment_variables_preview->where('key', $item->get('key'))->first(); if ($env) { @@ -622,7 +545,6 @@ public function delete_env_by_uuid(Request $request) { ray()->clearAll(); $teamId = get_team_id_from_token(); - $both = $request->query->get('both') ?? false; if (is_null($teamId)) { return invalid_token(); } @@ -642,12 +564,6 @@ public function delete_env_by_uuid(Request $request) ], 404); } $found_env->delete(); - if ($both) { - $found_other_pair = EnvironmentVariable::where('application_id', $application->id)->where('key', $found_env->key)->first(); - if ($found_other_pair) { - $found_other_pair->delete(); - } - } return response()->json([ 'success' => true, diff --git a/app/Http/Controllers/Api/EnvironmentVariables.php b/app/Http/Controllers/Api/EnvironmentVariables.php index d27d4f501..d788bdb0c 100644 --- a/app/Http/Controllers/Api/EnvironmentVariables.php +++ b/app/Http/Controllers/Api/EnvironmentVariables.php @@ -12,7 +12,6 @@ public function delete_env_by_uuid(Request $request) { ray()->clearAll(); $teamId = get_team_id_from_token(); - $both = $request->query->get('both') ?? false; if (is_null($teamId)) { return invalid_token(); } @@ -31,12 +30,6 @@ public function delete_env_by_uuid(Request $request) ], 404); } $env->delete(); - if ($both) { - $found_other_pair = EnvironmentVariable::where('application_id', $found_app->id)->where('key', $env->key)->first(); - if ($found_other_pair) { - $found_other_pair->delete(); - } - } return response()->json([ 'success' => true, From 4fb37054df557074ed58b9951fad1f7587dacc0a Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 26 Jun 2024 13:59:41 +0200 Subject: [PATCH 14/54] feat: Update server settings metrics history days to 7 --- .../migrations/2024_06_25_184323_update_db.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/database/migrations/2024_06_25_184323_update_db.php b/database/migrations/2024_06_25_184323_update_db.php index ef6ee74f7..c57c4d403 100644 --- a/database/migrations/2024_06_25_184323_update_db.php +++ b/database/migrations/2024_06_25_184323_update_db.php @@ -1,6 +1,7 @@ string('uuid')->nullable(false)->change(); }); + Schema::table('server_settings', function (Blueprint $table) { + $table->integer('metrics_history_days')->default(7)->change(); + }); + Server::all()->each(function (Server $server) { + $server->settings->update([ + 'metrics_history_days' => 7, + ]); + }); } /** @@ -45,6 +54,13 @@ public function down(): void Schema::table('environment_variables', function (Blueprint $table) { $table->dropColumn('uuid'); }); - + Schema::table('server_settings', function (Blueprint $table) { + $table->integer('metrics_history_days')->default(30)->change(); + }); + Server::all()->each(function (Server $server) { + $server->settings->update([ + 'metrics_history_days' => 30, + ]); + }); } }; From 613e980267f0325c3f4650e68868bfe5398a5962 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 27 Jun 2024 12:48:37 +0200 Subject: [PATCH 15/54] fix: cleanup subs in cloud --- .../Commands/CloudCleanupSubscriptions.php | 92 +++++++++++++++++++ app/Http/Controllers/Webhook/Stripe.php | 2 +- .../2024_06_25_184323_update_db.php | 26 ++++++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 app/Console/Commands/CloudCleanupSubscriptions.php diff --git a/app/Console/Commands/CloudCleanupSubscriptions.php b/app/Console/Commands/CloudCleanupSubscriptions.php new file mode 100644 index 000000000..0ef775dda --- /dev/null +++ b/app/Console/Commands/CloudCleanupSubscriptions.php @@ -0,0 +1,92 @@ +clearAll(); + $this->info('Cleaning up subcriptions teams'); + $stripe = new \Stripe\StripeClient(config('subscription.stripe_api_key')); + + $teams = Team::all()->sortBy('id'); + foreach ($teams as $team) { + $this->info("Checking team {$team->id}"); + if (! data_get($team, 'subscription')) { + $this->disableServers($team); + + continue; + } + // If the team has no subscription id and the invoice is paid, we need to reset the invoice paid status + if (! (data_get($team, 'subscription.stripe_subscription_id'))) { + $this->info("Resetting invoice paid status for team {$team->id} {$team->name}"); + + $team->subscription->update([ + 'stripe_invoice_paid' => false, + 'stripe_trial_already_ended' => false, + 'stripe_subscription_id' => null, + ]); + $this->disableServers($team); + + continue; + } else { + $subscription = $stripe->subscriptions->retrieve(data_get($team, 'subscription.stripe_subscription_id'), []); + $status = data_get($subscription, 'status'); + if ($status === 'active' || $status === 'past_due') { + $team->subscription->update([ + 'stripe_invoice_paid' => true, + 'stripe_trial_already_ended' => false, + ]); + + continue; + } + $this->info('Subscription status: '.$status); + $this->info('Subscription id: '.data_get($team, 'subscription.stripe_subscription_id')); + $confirm = $this->confirm('Do you want to cancel the subscription?', true); + if (! $confirm) { + $this->info("Skipping team {$team->id} {$team->name}"); + } else { + $this->info("Cancelling subscription for team {$team->id} {$team->name}"); + $team->subscription->update([ + 'stripe_invoice_paid' => false, + 'stripe_trial_already_ended' => false, + 'stripe_subscription_id' => null, + ]); + $this->disableServers($team); + } + } + } + + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return; + } + } + + private function disableServers(Team $team) + { + foreach ($team->servers as $server) { + if ($server->settings->is_usable === true || $server->settings->is_reachable === true || $server->ip !== '1.2.3.4') { + $this->info("Disabling server {$server->id} {$server->name}"); + $server->settings()->update([ + 'is_usable' => false, + 'is_reachable' => false, + ]); + $server->update([ + 'ip' => '1.2.3.4', + ]); + } + } + + } +} diff --git a/app/Http/Controllers/Webhook/Stripe.php b/app/Http/Controllers/Webhook/Stripe.php index 230525c0e..15484f7f3 100644 --- a/app/Http/Controllers/Webhook/Stripe.php +++ b/app/Http/Controllers/Webhook/Stripe.php @@ -231,7 +231,7 @@ public function events(Request $request) 'stripe_plan_id' => null, 'stripe_cancel_at_period_end' => false, 'stripe_invoice_paid' => false, - 'stripe_trial_already_ended' => true, + 'stripe_trial_already_ended' => false, ]); // send_internal_notification('customer.subscription.deleted for customer: '.$customerId); break; diff --git a/database/migrations/2024_06_25_184323_update_db.php b/database/migrations/2024_06_25_184323_update_db.php index c57c4d403..005e063cc 100644 --- a/database/migrations/2024_06_25_184323_update_db.php +++ b/database/migrations/2024_06_25_184323_update_db.php @@ -19,6 +19,19 @@ public function up(): void $table->dropColumn('docker_compose_pr'); $table->dropColumn('docker_compose_pr_raw'); }); + Schema::table('subscriptions', function (Blueprint $table) { + $table->dropColumn('lemon_subscription_id'); + $table->dropColumn('lemon_order_id'); + $table->dropColumn('lemon_product_id'); + $table->dropColumn('lemon_variant_id'); + $table->dropColumn('lemon_variant_name'); + $table->dropColumn('lemon_customer_id'); + $table->dropColumn('lemon_status'); + $table->dropColumn('lemon_renews_at'); + $table->dropColumn('lemon_update_payment_menthod_url'); + $table->dropColumn('lemon_trial_ends_at'); + $table->dropColumn('lemon_ends_at'); + }); Schema::table('environment_variables', function (Blueprint $table) { $table->string('uuid')->nullable()->after('id'); }); @@ -51,6 +64,19 @@ public function down(): void $table->longText('docker_compose_pr')->nullable()->after('docker_compose_location'); $table->longText('docker_compose_pr_raw')->nullable()->after('docker_compose'); }); + Schema::table('subscriptions', function (Blueprint $table) { + $table->string('lemon_subscription_id')->nullable()->after('stripe_subscription_id'); + $table->string('lemon_order_id')->nullable()->after('lemon_subscription_id'); + $table->string('lemon_product_id')->nullable()->after('lemon_order_id'); + $table->string('lemon_variant_id')->nullable()->after('lemon_product_id'); + $table->string('lemon_variant_name')->nullable()->after('lemon_variant_id'); + $table->string('lemon_customer_id')->nullable()->after('lemon_variant_name'); + $table->string('lemon_status')->nullable()->after('lemon_customer_id'); + $table->timestamp('lemon_renews_at')->nullable()->after('lemon_status'); + $table->string('lemon_update_payment_menthod_url')->nullable()->after('lemon_renews_at'); + $table->timestamp('lemon_trial_ends_at')->nullable()->after('lemon_update_payment_menthod_url'); + $table->timestamp('lemon_ends_at')->nullable()->after('lemon_trial_ends_at'); + }); Schema::table('environment_variables', function (Blueprint $table) { $table->dropColumn('uuid'); }); From be633f05608781ecf5c666a6962a270b6f4bb48e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 27 Jun 2024 15:07:41 +0200 Subject: [PATCH 16/54] fix: only run cloud clean on cloud + remove root team --- app/Console/Commands/CloudCleanupSubscriptions.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/Console/Commands/CloudCleanupSubscriptions.php b/app/Console/Commands/CloudCleanupSubscriptions.php index 0ef775dda..1d9dc5d74 100644 --- a/app/Console/Commands/CloudCleanupSubscriptions.php +++ b/app/Console/Commands/CloudCleanupSubscriptions.php @@ -14,13 +14,22 @@ class CloudCleanupSubs extends Command public function handle() { try { + if (! isCloud()) { + $this->error('This command can only be run on cloud'); + + return; + } ray()->clearAll(); $this->info('Cleaning up subcriptions teams'); $stripe = new \Stripe\StripeClient(config('subscription.stripe_api_key')); - $teams = Team::all()->sortBy('id'); + $teams = Team::all()->filter(function ($team) { + return $team->id !== 0; + })->sortBy('id'); foreach ($teams as $team) { - $this->info("Checking team {$team->id}"); + if ($team) { + $this->info("Checking team {$team->id}"); + } if (! data_get($team, 'subscription')) { $this->disableServers($team); From ca917d9d2158c95e61b9871c01062a491c8915ae Mon Sep 17 00:00:00 2001 From: Benjamin Rumble Date: Thu, 27 Jun 2024 11:30:28 -0400 Subject: [PATCH 17/54] fix minor typo in backup.blade.php ~add as a database~ -> add a database --- resources/views/livewire/settings/backup.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/livewire/settings/backup.blade.php b/resources/views/livewire/settings/backup.blade.php index 50f5f3d28..d517b9516 100644 --- a/resources/views/livewire/settings/backup.blade.php +++ b/resources/views/livewire/settings/backup.blade.php @@ -24,7 +24,7 @@ @else - To configure automatic backup for your Coolify instance, you first need to add as a database resource + To configure automatic backup for your Coolify instance, you first need to add a database resource into Coolify. Add Database @endif From 70bfd4dd8a3effbd854dba963e528c50d9cdf026 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 28 Jun 2024 11:00:02 +0200 Subject: [PATCH 18/54] fix: show keydbs/dragonflies/clickhouses --- app/Models/Environment.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Models/Environment.php b/app/Models/Environment.php index b2bb51092..fc19c134f 100644 --- a/app/Models/Environment.php +++ b/app/Models/Environment.php @@ -27,6 +27,9 @@ public function isEmpty() $this->redis()->count() == 0 && $this->postgresqls()->count() == 0 && $this->mysqls()->count() == 0 && + $this->keydbs()->count() == 0 && + $this->dragonflies()->count() == 0 && + $this->clickhouses()->count() == 0 && $this->mariadbs()->count() == 0 && $this->mongodbs()->count() == 0 && $this->services()->count() == 0; From 2dd17cfac5fa2f1502878298eccd041f0c823684 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 28 Jun 2024 12:03:38 +0200 Subject: [PATCH 19/54] fix: force cleanup on busy servers --- app/Jobs/DockerCleanupJob.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/Jobs/DockerCleanupJob.php b/app/Jobs/DockerCleanupJob.php index e637fb6d4..e42caeead 100644 --- a/app/Jobs/DockerCleanupJob.php +++ b/app/Jobs/DockerCleanupJob.php @@ -22,7 +22,9 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue public ?int $usageBefore = null; - public function __construct(public Server $server) {} + public function __construct(public Server $server) + { + } public function handle(): void { @@ -35,9 +37,9 @@ public function handle(): void return; } }); - if ($isInprogress) { - throw new RuntimeException('DockerCleanupJob: ApplicationDeploymentQueue is not empty, skipping...'); - } + // if ($isInprogress) { + // throw new RuntimeException('DockerCleanupJob: ApplicationDeploymentQueue is not empty, skipping...'); + // } if (! $this->server->isFunctional()) { return; } From e3c4ebb12189ae80acdcff509b715d010e9f25fe Mon Sep 17 00:00:00 2001 From: andrasbacsai Date: Fri, 28 Jun 2024 10:04:28 +0000 Subject: [PATCH 20/54] Fix styling --- app/Jobs/DockerCleanupJob.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/Jobs/DockerCleanupJob.php b/app/Jobs/DockerCleanupJob.php index e42caeead..785940ee6 100644 --- a/app/Jobs/DockerCleanupJob.php +++ b/app/Jobs/DockerCleanupJob.php @@ -22,9 +22,7 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue public ?int $usageBefore = null; - public function __construct(public Server $server) - { - } + public function __construct(public Server $server) {} public function handle(): void { From 30b7e831c0ddb15f52d781eb58a43c54e45e8523 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 28 Jun 2024 15:05:37 +0200 Subject: [PATCH 21/54] feat: new app API endpoint --- app/Enums/BuildPackTypes.php | 11 + app/Enums/NewResourceTypes.php | 22 ++ app/Http/Controllers/Api/Applications.php | 235 +++++++++++------- .../Api/{Server.php => Servers.php} | 2 +- app/Models/Server.php | 4 +- bootstrap/helpers/api.php | 117 +++++++++ routes/api.php | 14 +- 7 files changed, 300 insertions(+), 105 deletions(-) create mode 100644 app/Enums/BuildPackTypes.php create mode 100644 app/Enums/NewResourceTypes.php rename app/Http/Controllers/Api/{Server.php => Servers.php} (99%) diff --git a/app/Enums/BuildPackTypes.php b/app/Enums/BuildPackTypes.php new file mode 100644 index 000000000..d4fd505d2 --- /dev/null +++ b/app/Enums/BuildPackTypes.php @@ -0,0 +1,11 @@ +json(serialize_api_response($applications)); } + public function create_application(Request $request) + { + + ray()->clearAll(); + $allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'instant_deploy']; + $teamId = get_team_id_from_token(); + if (is_null($teamId)) { + return invalid_token(); + } + if (! $request->isJson()) { + return response()->json([ + 'message' => 'Invalid request.', + 'error' => 'Content-Type must be application/json.', + ], 400); + } + // check if request is valid json + if (! json_decode($request->getContent())) { + return response()->json([ + 'message' => 'Invalid request.', + 'error' => 'Invalid JSON.', + ], 400); + } + $validator = customApiValidator($request->all(), [ + 'name' => 'string|max:255', + 'description' => 'string|nullable', + 'project_uuid' => 'string|required', + 'environment_name' => 'string|required', + 'server_uuid' => 'string|required', + 'destination_uuid' => 'string', + 'type' => ['required', Rule::enum(NewResourceTypes::class)], + ]); + + $extraFields = array_diff(array_keys($request->all()), $allowedFields); + if ($validator->fails() || ! empty($extraFields)) { + $errors = $validator->errors(); + if (! empty($extraFields)) { + foreach ($extraFields as $field) { + $errors->add($field, 'This field is not allowed.'); + } + } + + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + $serverUuid = $request->server_uuid; + $fqdn = $request->domains; + $type = $request->type; + $instantDeploy = $request->instant_deploy; + $project = Project::whereTeamId($teamId)->whereUuid($request->project_uuid)->first(); + if (! $project) { + return response()->json(['error' => 'Project not found.'], 404); + } + $environment = $project->environments()->where('name', $request->environment_name)->first(); + if (! $environment) { + return response()->json(['error' => 'Environment not found.'], 404); + } + if (! $request->has('name')) { + $request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch)); + } + $server = Server::whereTeamId($teamId)->whereUuid($serverUuid)->first(); + if (! $server) { + return response()->json(['error' => 'Server not found.'], 404); + } + $destinations = $server->destinations(); + if ($destinations->count() == 0) { + return response()->json(['error' => 'Server has no destinations.'], 400); + } + if ($destinations->count() > 1 && ! $request->has('destination_uuid')) { + return response()->json(['error' => 'Server has multiple destinations and you do not set destination_uuid.'], 400); + } + $destination = $destinations->first(); + if ($type === 'public') { + $validator = customApiValidator($request->all(), [ + sharedDataApplications(), + 'git_repository' => 'string|required', + 'git_branch' => 'string|required', + 'build_pack' => ['required', Rule::enum(BuildPackTypes::class)], + 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required', + ]); + if ($validator->fails()) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $validator->errors(), + ], 422); + } + $return = validateDataApplications($request, $server); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } + $application = new Application(); + $request->offsetUnset('project_uuid'); + $request->offsetUnset('environment_name'); + $request->offsetUnset('destination_uuid'); + $request->offsetUnset('server_uuid'); + $request->offsetUnset('type'); + $request->offsetUnset('domains'); + $request->offsetUnset('instant_deploy'); + + $application->fill($request->all()); + + $application->fqdn = $fqdn; + $application->destination_id = $destination->id; + $application->destination_type = $destination->getMorphClass(); + $application->environment_id = $environment->id; + $application->save(); + + if ($instantDeploy) { + $deployment_uuid = new Cuid2(7); + + queue_application_deployment( + application: $application, + deployment_uuid: $deployment_uuid, + no_questions_asked: true, + is_api: true, + ); + } + + return response()->json(serialize_api_response($application)); + } + + return response()->json('Application created')->setStatusCode(201); + + } + public function application_by_uuid(Request $request) { $teamId = get_team_id_from_token(); @@ -99,63 +227,20 @@ public function update_by_uuid(Request $request) ], 404); } $server = $application->destination->server; - $allowedFields = ['name', 'description', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'redirect']; + $allowedFields = ['name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'redirect']; $validator = customApiValidator($request->all(), [ + sharedDataApplications(), 'name' => 'string|max:255', 'description' => 'string|nullable', - 'domains' => 'string', - 'git_repository' => 'string', - 'git_branch' => 'string', - 'git_commit_sha' => 'string', - 'docker_registry_image_name' => 'string|nullable', - 'docker_registry_image_tag' => 'string|nullable', - 'build_pack' => 'string', 'static_image' => 'string', - 'install_command' => 'string|nullable', - 'build_command' => 'string|nullable', - 'start_command' => 'string|nullable', - 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/', - 'ports_mappings' => 'string|regex:/^(\d+:\d+)(,\d+:\d+)*$/|nullable', - 'base_directory' => 'string|nullable', - 'publish_directory' => 'string|nullable', - 'health_check_enabled' => 'boolean', - 'health_check_path' => 'string', - 'health_check_port' => 'string|nullable', - 'health_check_host' => 'string', - 'health_check_method' => 'string', - 'health_check_return_code' => 'numeric', - 'health_check_scheme' => 'string', - 'health_check_response_text' => 'string|nullable', - 'health_check_interval' => 'numeric', - 'health_check_timeout' => 'numeric', - 'health_check_retries' => 'numeric', - 'health_check_start_period' => 'numeric', - 'limits_memory' => 'string', - 'limits_memory_swap' => 'string', - 'limits_memory_swappiness' => 'numeric', - 'limits_memory_reservation' => 'string', - 'limits_cpus' => 'string', - 'limits_cpuset' => 'string|nullable', - 'limits_cpu_shares' => 'numeric', - 'custom_labels' => 'string|nullable', - 'custom_docker_run_options' => 'string|nullable', - 'post_deployment_command' => 'string|nullable', - 'post_deployment_command_container' => 'string', - 'pre_deployment_command' => 'string|nullable', - 'pre_deployment_command_container' => 'string', 'watch_paths' => 'string|nullable', - 'manual_webhook_secret_github' => 'string|nullable', - 'manual_webhook_secret_gitlab' => 'string|nullable', - 'manual_webhook_secret_bitbucket' => 'string|nullable', - 'manual_webhook_secret_gitea' => 'string|nullable', 'docker_compose_location' => 'string', 'docker_compose' => 'string|nullable', 'docker_compose_raw' => 'string|nullable', // 'docker_compose_domains' => 'string|nullable', // must be like: "{\"api\":{\"domain\":\"http:\\/\\/b8sos8k.127.0.0.1.sslip.io\"}}" 'docker_compose_custom_start_command' => 'string|nullable', 'docker_compose_custom_build_command' => 'string|nullable', - 'redirect' => Rule::enum(RedirectTypes::class), ]); // Validate ports_exposes @@ -172,42 +257,9 @@ public function update_by_uuid(Request $request) } } } - // Validate ports_mappings - if ($request->has('ports_mappings')) { - $ports = []; - foreach (explode(',', $request->ports_mappings) as $portMapping) { - $port = explode(':', $portMapping); - if (in_array($port[0], $ports)) { - return response()->json([ - 'message' => 'Validation failed.', - 'errors' => [ - 'ports_mappings' => 'The first number before : should be unique between mappings.', - ], - ], 422); - } - $ports[] = $port[0]; - } - } - // Validate custom_labels - if ($request->has('custom_labels')) { - if (! isBase64Encoded($request->custom_labels)) { - return response()->json([ - 'message' => 'Validation failed.', - 'errors' => [ - 'custom_labels' => 'The custom_labels should be base64 encoded.', - ], - ], 422); - } - $customLabels = base64_decode($request->custom_labels); - if (mb_detect_encoding($customLabels, 'ASCII', true) === false) { - return response()->json([ - 'message' => 'Validation failed.', - 'errors' => [ - 'custom_labels' => 'The custom_labels should be base64 encoded.', - ], - ], 422); - - } + $return = validateDataApplications($request, $server); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; } $extraFields = array_diff(array_keys($request->all()), $allowedFields); if ($validator->fails() || ! empty($extraFields)) { @@ -223,31 +275,22 @@ public function update_by_uuid(Request $request) 'errors' => $errors, ], 422); } + $domains = $request->domains; if ($request->has('domains') && $server->isProxyShouldRun()) { $fqdn = $request->domains; $fqdn = str($fqdn)->replaceEnd(',', '')->trim(); $fqdn = str($fqdn)->replaceStart(',', '')->trim(); $errors = []; - $fqdn = str($fqdn)->trim()->explode(',')->map(function ($domain) use (&$errors) { - if (filter_var($domain, FILTER_VALIDATE_URL) === false) { - $errors[] = 'Invalid domain: '.$domain; - } - - return str($domain)->trim()->lower(); - }); - if (count($errors) > 0) { - return response()->json([ - 'message' => 'Validation failed.', - 'errors' => $errors, - ], 422); - } $fqdn = $fqdn->unique()->implode(','); $application->fqdn = $fqdn; $customLabels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n"); $application->custom_labels = base64_encode($customLabels); $request->offsetUnset('domains'); } - $application->fill($request->all()); + + $data = $request->all(); + data_set($data, 'fqdn', $domains); + $application->fill($data); $application->save(); return response()->json(serialize_api_response($application)); diff --git a/app/Http/Controllers/Api/Server.php b/app/Http/Controllers/Api/Servers.php similarity index 99% rename from app/Http/Controllers/Api/Server.php rename to app/Http/Controllers/Api/Servers.php index 1a58da7b0..387c4bd48 100644 --- a/app/Http/Controllers/Api/Server.php +++ b/app/Http/Controllers/Api/Servers.php @@ -9,7 +9,7 @@ use App\Models\Server as ModelsServer; use Illuminate\Http\Request; -class Server extends Controller +class Servers extends Controller { public function servers(Request $request) { diff --git a/app/Models/Server.php b/app/Models/Server.php index 29eedd59f..cd6cc9890 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -496,7 +496,7 @@ public function checkServerApi() public function checkSentinel() { - ray("Checking sentinel on server: {$this->name}"); + // ray("Checking sentinel on server: {$this->name}"); if ($this->isSentinelEnabled()) { $sentinel_found = instant_remote_process(['docker inspect coolify-sentinel'], $this, false); $sentinel_found = json_decode($sentinel_found, true); @@ -505,7 +505,7 @@ public function checkSentinel() ray('Sentinel is not running, starting it...'); PullSentinelImageJob::dispatch($this); } else { - ray('Sentinel is running'); + // ray('Sentinel is running'); } } } diff --git a/bootstrap/helpers/api.php b/bootstrap/helpers/api.php index c278a5045..a0e42772e 100644 --- a/bootstrap/helpers/api.php +++ b/bootstrap/helpers/api.php @@ -1,6 +1,11 @@ 'string', + 'git_branch' => 'string', + 'build_pack' => Rule::enum(BuildPackTypes::class), + 'is_static' => 'boolean', + 'domains' => 'string', + 'redirect' => Rule::enum(RedirectTypes::class), + 'git_commit_sha' => 'string', + 'docker_registry_image_name' => 'string|nullable', + 'docker_registry_image_tag' => 'string|nullable', + 'install_command' => 'string|nullable', + 'build_command' => 'string|nullable', + 'start_command' => 'string|nullable', + 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/', + 'ports_mappings' => 'string|regex:/^(\d+:\d+)(,\d+:\d+)*$/|nullable', + 'base_directory' => 'string|nullable', + 'publish_directory' => 'string|nullable', + 'health_check_enabled' => 'boolean', + 'health_check_path' => 'string', + 'health_check_port' => 'string|nullable', + 'health_check_host' => 'string', + 'health_check_method' => 'string', + 'health_check_return_code' => 'numeric', + 'health_check_scheme' => 'string', + 'health_check_response_text' => 'string|nullable', + 'health_check_interval' => 'numeric', + 'health_check_timeout' => 'numeric', + 'health_check_retries' => 'numeric', + 'health_check_start_period' => 'numeric', + 'limits_memory' => 'string', + 'limits_memory_swap' => 'string', + 'limits_memory_swappiness' => 'numeric', + 'limits_memory_reservation' => 'string', + 'limits_cpus' => 'string', + 'limits_cpuset' => 'string|nullable', + 'limits_cpu_shares' => 'numeric', + 'custom_labels' => 'string|nullable', + 'custom_docker_run_options' => 'string|nullable', + 'post_deployment_command' => 'string|nullable', + 'post_deployment_command_container' => 'string', + 'pre_deployment_command' => 'string|nullable', + 'pre_deployment_command_container' => 'string', + 'manual_webhook_secret_github' => 'string|nullable', + 'manual_webhook_secret_gitlab' => 'string|nullable', + 'manual_webhook_secret_bitbucket' => 'string|nullable', + 'manual_webhook_secret_gitea' => 'string|nullable', + ]; +} + +function validateDataApplications(Request $request, Server $server) +{ + // Validate ports_mappings + if ($request->has('ports_mappings')) { + $ports = []; + foreach (explode(',', $request->ports_mappings) as $portMapping) { + $port = explode(':', $portMapping); + if (in_array($port[0], $ports)) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => [ + 'ports_mappings' => 'The first number before : should be unique between mappings.', + ], + ], 422); + } + $ports[] = $port[0]; + } + } + // Validate custom_labels + if ($request->has('custom_labels')) { + if (! isBase64Encoded($request->custom_labels)) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => [ + 'custom_labels' => 'The custom_labels should be base64 encoded.', + ], + ], 422); + } + $customLabels = base64_decode($request->custom_labels); + if (mb_detect_encoding($customLabels, 'ASCII', true) === false) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => [ + 'custom_labels' => 'The custom_labels should be base64 encoded.', + ], + ], 422); + + } + } + if ($request->has('domains') && $server->isProxyShouldRun()) { + $fqdn = $request->domains; + $fqdn = str($fqdn)->replaceEnd(',', '')->trim(); + $fqdn = str($fqdn)->replaceStart(',', '')->trim(); + $errors = []; + $fqdn = str($fqdn)->trim()->explode(',')->map(function ($domain) use (&$errors) { + ray(filter_var($domain, FILTER_VALIDATE_URL)); + if (filter_var($domain, FILTER_VALIDATE_URL) === false) { + $errors[] = 'Invalid domain: '.$domain; + } + + return str($domain)->trim()->lower(); + }); + if (count($errors) > 0) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + } +} diff --git a/routes/api.php b/routes/api.php index 7aca146ba..f4d9d786e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -4,7 +4,7 @@ use App\Http\Controllers\Api\Deploy; use App\Http\Controllers\Api\EnvironmentVariables; use App\Http\Controllers\Api\Resources; -use App\Http\Controllers\Api\Server; +use App\Http\Controllers\Api\Servers; use App\Http\Controllers\Api\Team; use Illuminate\Http\Request; use Illuminate\Support\Facades\Http; @@ -36,13 +36,15 @@ Route::get('/deployments', [Deploy::class, 'deployments']); Route::get('/deployments/{uuid}', [Deploy::class, 'deployment_by_uuid']); - Route::get('/servers', [Server::class, 'servers']); - Route::get('/servers/{uuid}', [Server::class, 'server_by_uuid']); - Route::get('/servers/domains', [Server::class, 'get_domains_by_server']); + // Add environments endpoints + Route::get('/servers', [Servers::class, 'servers']); + Route::get('/servers/{uuid}', [Servers::class, 'server_by_uuid']); + Route::get('/servers/domains', [Servers::class, 'get_domains_by_server']); Route::get('/resources', [Resources::class, 'resources']); Route::get('/applications', [Applications::class, 'applications']); + Route::post('/applications', [Applications::class, 'create_application']); Route::get('/applications/{uuid}', [Applications::class, 'application_by_uuid']); Route::patch('/applications/{uuid}', [Applications::class, 'update_by_uuid']); @@ -54,12 +56,12 @@ Route::patch('/applications/{uuid}/envs', [Applications::class, 'update_env_by_uuid']); Route::delete('/applications/{uuid}/envs/{env_uuid}', [Applications::class, 'delete_env_by_uuid']); - Route::delete('/envs/{env_uuid}', [EnvironmentVariables::class, 'delete_env_by_uuid']); - Route::match(['get', 'post'], '/applications/{uuid}/action/deploy', [Applications::class, 'action_deploy']); Route::match(['get', 'post'], '/applications/{uuid}/action/restart', [Applications::class, 'action_restart']); Route::match(['get', 'post'], '/applications/{uuid}/action/stop', [Applications::class, 'action_stop']); + Route::delete('/envs/{env_uuid}', [EnvironmentVariables::class, 'delete_env_by_uuid']); + Route::get('/teams', [Team::class, 'teams']); Route::get('/teams/current', [Team::class, 'current_team']); Route::get('/teams/current/members', [Team::class, 'current_team_members']); From b86924bc0e5b838c6709350b8bdb733e5e77e7fd Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Sun, 30 Jun 2024 11:30:31 +0200 Subject: [PATCH 22/54] feat: private gh deployments through api --- app/Http/Controllers/Api/Applications.php | 194 ++++++++++++++++++++-- app/Models/GithubApp.php | 21 ++- bootstrap/helpers/api.php | 63 ------- 3 files changed, 196 insertions(+), 82 deletions(-) diff --git a/app/Http/Controllers/Api/Applications.php b/app/Http/Controllers/Api/Applications.php index 6fe340176..0e651f476 100644 --- a/app/Http/Controllers/Api/Applications.php +++ b/app/Http/Controllers/Api/Applications.php @@ -9,6 +9,8 @@ use App\Jobs\DeleteResourceJob; use App\Models\Application; use App\Models\EnvironmentVariable; +use App\Models\GithubApp; +use App\Models\PrivateKey; use App\Models\Project; use App\Models\Server; use Illuminate\Http\Request; @@ -35,7 +37,7 @@ public function create_application(Request $request) { ray()->clearAll(); - $allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'instant_deploy']; + $allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy']; $teamId = get_team_id_from_token(); if (is_null($teamId)) { return invalid_token(); @@ -77,10 +79,13 @@ public function create_application(Request $request) 'errors' => $errors, ], 422); } + $serverUuid = $request->server_uuid; $fqdn = $request->domains; $type = $request->type; $instantDeploy = $request->instant_deploy; + $githubAppUuid = $request->github_app_uuid; + $project = Project::whereTeamId($teamId)->whereUuid($request->project_uuid)->first(); if (! $project) { return response()->json(['error' => 'Project not found.'], 404); @@ -118,18 +123,12 @@ public function create_application(Request $request) 'errors' => $validator->errors(), ], 422); } - $return = validateDataApplications($request, $server); + $return = $this->validateDataApplications($request, $server); if ($return instanceof \Illuminate\Http\JsonResponse) { return $return; } $application = new Application(); - $request->offsetUnset('project_uuid'); - $request->offsetUnset('environment_name'); - $request->offsetUnset('destination_uuid'); - $request->offsetUnset('server_uuid'); - $request->offsetUnset('type'); - $request->offsetUnset('domains'); - $request->offsetUnset('instant_deploy'); + $this->removeUnnecessaryFieldsFromRequest($request); $application->fill($request->all()); @@ -150,10 +149,110 @@ public function create_application(Request $request) ); } + return response()->json(serialize_api_response($application)); + } elseif ($type === 'private-gh-app') { + $validator = customApiValidator($request->all(), [ + sharedDataApplications(), + 'git_repository' => 'string|required', + 'git_branch' => 'string|required', + 'build_pack' => ['required', Rule::enum(BuildPackTypes::class)], + 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required', + 'github_app_uuid' => 'string|required', + ]); + if ($validator->fails()) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $validator->errors(), + ], 422); + } + $return = $this->validateDataApplications($request, $server); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } + $githubApp = GithubApp::whereTeamId($teamId)->where('uuid', $githubAppUuid)->first(); + if (! $githubApp) { + return response()->json(['error' => 'Github App not found.'], 404); + } + $gitRepository = $request->git_repository; + if (str($gitRepository)->startsWith('http') || str($gitRepository)->contains('github.com')) { + $gitRepository = str($gitRepository)->replace('https://', '')->replace('http://', '')->replace('github.com/', ''); + } + $application = new Application(); + $this->removeUnnecessaryFieldsFromRequest($request); + + $application->fill($request->all()); + + $application->fqdn = $fqdn; + $application->git_repository = $gitRepository; + $application->destination_id = $destination->id; + $application->destination_type = $destination->getMorphClass(); + $application->environment_id = $environment->id; + $application->source_type = $githubApp->getMorphClass(); + $application->source_id = $githubApp->id; + $application->save(); + + if ($instantDeploy) { + $deployment_uuid = new Cuid2(7); + + queue_application_deployment( + application: $application, + deployment_uuid: $deployment_uuid, + no_questions_asked: true, + is_api: true, + ); + } + + return response()->json(serialize_api_response($application)); + } elseif ($type === 'private-deploy-key') { + $validator = customApiValidator($request->all(), [ + sharedDataApplications(), + 'git_repository' => 'string|required', + 'git_branch' => 'string|required', + 'build_pack' => ['required', Rule::enum(BuildPackTypes::class)], + 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required', + 'private_key_uuid' => 'string|required', + ]); + if ($validator->fails()) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $validator->errors(), + ], 422); + } + $return = $this->validateDataApplications($request, $server); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } + $privateKey = PrivateKey::whereTeamId($teamId)->where('uuid', $request->private_key_uuid)->first(); + if (! $privateKey) { + return response()->json(['error' => 'Private Key not found.'], 404); + } + + $application = new Application(); + $this->removeUnnecessaryFieldsFromRequest($request); + + $application->fill($request->all()); + $application->fqdn = $fqdn; + $application->private_key_id = $privateKey->id; + $application->destination_id = $destination->id; + $application->destination_type = $destination->getMorphClass(); + $application->environment_id = $environment->id; + $application->save(); + + if ($instantDeploy) { + $deployment_uuid = new Cuid2(7); + + queue_application_deployment( + application: $application, + deployment_uuid: $deployment_uuid, + no_questions_asked: true, + is_api: true, + ); + } + return response()->json(serialize_api_response($application)); } - return response()->json('Application created')->setStatusCode(201); + return response()->json(['error' => 'Invalid type.'], 400); } @@ -257,7 +356,7 @@ public function update_by_uuid(Request $request) } } } - $return = validateDataApplications($request, $server); + $return = $this->validateDataApplications($request, $server); if ($return instanceof \Illuminate\Http\JsonResponse) { return $return; } @@ -711,4 +810,77 @@ public function action_restart(Request $request) ); } + + private function removeUnnecessaryFieldsFromRequest(Request $request) + { + $request->offsetUnset('project_uuid'); + $request->offsetUnset('environment_name'); + $request->offsetUnset('destination_uuid'); + $request->offsetUnset('server_uuid'); + $request->offsetUnset('type'); + $request->offsetUnset('domains'); + $request->offsetUnset('instant_deploy'); + $request->offsetUnset('github_app_uuid'); + $request->offsetUnset('private_key_uuid'); + } + + private function validateDataApplications(Request $request, Server $server) + { + // Validate ports_mappings + if ($request->has('ports_mappings')) { + $ports = []; + foreach (explode(',', $request->ports_mappings) as $portMapping) { + $port = explode(':', $portMapping); + if (in_array($port[0], $ports)) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => [ + 'ports_mappings' => 'The first number before : should be unique between mappings.', + ], + ], 422); + } + $ports[] = $port[0]; + } + } + // Validate custom_labels + if ($request->has('custom_labels')) { + if (! isBase64Encoded($request->custom_labels)) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => [ + 'custom_labels' => 'The custom_labels should be base64 encoded.', + ], + ], 422); + } + $customLabels = base64_decode($request->custom_labels); + if (mb_detect_encoding($customLabels, 'ASCII', true) === false) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => [ + 'custom_labels' => 'The custom_labels should be base64 encoded.', + ], + ], 422); + + } + } + if ($request->has('domains') && $server->isProxyShouldRun()) { + $fqdn = $request->domains; + $fqdn = str($fqdn)->replaceEnd(',', '')->trim(); + $fqdn = str($fqdn)->replaceStart(',', '')->trim(); + $errors = []; + $fqdn = str($fqdn)->trim()->explode(',')->map(function ($domain) use (&$errors) { + if (filter_var($domain, FILTER_VALIDATE_URL) === false) { + $errors[] = 'Invalid domain: '.$domain; + } + + return str($domain)->trim()->lower(); + }); + if (count($errors) > 0) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + } + } } diff --git a/app/Models/GithubApp.php b/app/Models/GithubApp.php index daf902daf..66ecdd967 100644 --- a/app/Models/GithubApp.php +++ b/app/Models/GithubApp.php @@ -20,6 +20,17 @@ class GithubApp extends BaseModel 'webhook_secret', ]; + protected static function booted(): void + { + static::deleting(function (GithubApp $github_app) { + $applications_count = Application::where('source_id', $github_app->id)->count(); + if ($applications_count > 0) { + throw new \Exception('You cannot delete this GitHub App because it is in use by '.$applications_count.' application(s). Delete them first.'); + } + $github_app->privateKey()->delete(); + }); + } + public static function public() { return GithubApp::whereTeamId(currentTeam()->id)->whereisPublic(true)->whereNotNull('app_id')->get(); @@ -30,15 +41,9 @@ public static function private() return GithubApp::whereTeamId(currentTeam()->id)->whereisPublic(false)->whereNotNull('app_id')->get(); } - protected static function booted(): void + public function team() { - static::deleting(function (GithubApp $github_app) { - $applications_count = Application::where('source_id', $github_app->id)->count(); - if ($applications_count > 0) { - throw new \Exception('You cannot delete this GitHub App because it is in use by '.$applications_count.' application(s). Delete them first.'); - } - $github_app->privateKey()->delete(); - }); + return $this->belongsTo(Team::class); } public function applications() diff --git a/bootstrap/helpers/api.php b/bootstrap/helpers/api.php index a0e42772e..f5c99dbda 100644 --- a/bootstrap/helpers/api.php +++ b/bootstrap/helpers/api.php @@ -2,9 +2,7 @@ use App\Enums\BuildPackTypes; use App\Enums\RedirectTypes; -use App\Models\Server; use Illuminate\Database\Eloquent\Collection; -use Illuminate\Http\Request; use Illuminate\Validation\Rule; function get_team_id_from_token() @@ -92,64 +90,3 @@ function sharedDataApplications() 'manual_webhook_secret_gitea' => 'string|nullable', ]; } - -function validateDataApplications(Request $request, Server $server) -{ - // Validate ports_mappings - if ($request->has('ports_mappings')) { - $ports = []; - foreach (explode(',', $request->ports_mappings) as $portMapping) { - $port = explode(':', $portMapping); - if (in_array($port[0], $ports)) { - return response()->json([ - 'message' => 'Validation failed.', - 'errors' => [ - 'ports_mappings' => 'The first number before : should be unique between mappings.', - ], - ], 422); - } - $ports[] = $port[0]; - } - } - // Validate custom_labels - if ($request->has('custom_labels')) { - if (! isBase64Encoded($request->custom_labels)) { - return response()->json([ - 'message' => 'Validation failed.', - 'errors' => [ - 'custom_labels' => 'The custom_labels should be base64 encoded.', - ], - ], 422); - } - $customLabels = base64_decode($request->custom_labels); - if (mb_detect_encoding($customLabels, 'ASCII', true) === false) { - return response()->json([ - 'message' => 'Validation failed.', - 'errors' => [ - 'custom_labels' => 'The custom_labels should be base64 encoded.', - ], - ], 422); - - } - } - if ($request->has('domains') && $server->isProxyShouldRun()) { - $fqdn = $request->domains; - $fqdn = str($fqdn)->replaceEnd(',', '')->trim(); - $fqdn = str($fqdn)->replaceStart(',', '')->trim(); - $errors = []; - $fqdn = str($fqdn)->trim()->explode(',')->map(function ($domain) use (&$errors) { - ray(filter_var($domain, FILTER_VALIDATE_URL)); - if (filter_var($domain, FILTER_VALIDATE_URL) === false) { - $errors[] = 'Invalid domain: '.$domain; - } - - return str($domain)->trim()->lower(); - }); - if (count($errors) > 0) { - return response()->json([ - 'message' => 'Validation failed.', - 'errors' => $errors, - ], 422); - } - } -} From dbc235d84a5ec7a5b008a19ce1da5e65fb9fbb77 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 1 Jul 2024 11:39:10 +0200 Subject: [PATCH 23/54] fix: check domain on new app via api --- app/Http/Controllers/Api/Applications.php | 93 ++++++++++++++++++++++- app/Models/Server.php | 2 +- app/Models/ServiceApplication.php | 5 ++ bootstrap/helpers/shared.php | 71 +++++++++++++++++ 4 files changed, 166 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/Api/Applications.php b/app/Http/Controllers/Api/Applications.php index 0e651f476..c73b894ba 100644 --- a/app/Http/Controllers/Api/Applications.php +++ b/app/Http/Controllers/Api/Applications.php @@ -37,7 +37,7 @@ public function create_application(Request $request) { ray()->clearAll(); - $allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy']; + $allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile']; $teamId = get_team_id_from_token(); if (is_null($teamId)) { return invalid_token(); @@ -94,9 +94,6 @@ public function create_application(Request $request) if (! $environment) { return response()->json(['error' => 'Environment not found.'], 404); } - if (! $request->has('name')) { - $request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch)); - } $server = Server::whereTeamId($teamId)->whereUuid($serverUuid)->first(); if (! $server) { return response()->json(['error' => 'Server not found.'], 404); @@ -110,6 +107,9 @@ public function create_application(Request $request) } $destination = $destinations->first(); if ($type === 'public') { + if (! $request->has('name')) { + $request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch)); + } $validator = customApiValidator($request->all(), [ sharedDataApplications(), 'git_repository' => 'string|required', @@ -151,6 +151,9 @@ public function create_application(Request $request) return response()->json(serialize_api_response($application)); } elseif ($type === 'private-gh-app') { + if (! $request->has('name')) { + $request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch)); + } $validator = customApiValidator($request->all(), [ sharedDataApplications(), 'git_repository' => 'string|required', @@ -204,6 +207,9 @@ public function create_application(Request $request) return response()->json(serialize_api_response($application)); } elseif ($type === 'private-deploy-key') { + if (! $request->has('name')) { + $request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch)); + } $validator = customApiValidator($request->all(), [ sharedDataApplications(), 'git_repository' => 'string|required', @@ -249,6 +255,75 @@ public function create_application(Request $request) ); } + return response()->json(serialize_api_response($application)); + } elseif ($type === 'dockerfile') { + if (! $request->has('name')) { + $request->offsetSet('name', 'dockerfile-'.new Cuid2(7)); + } + $validator = customApiValidator($request->all(), [ + sharedDataApplications(), + 'dockerfile' => 'string|required', + ]); + if ($validator->fails()) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $validator->errors(), + ], 422); + } + $return = $this->validateDataApplications($request, $server); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } + if (! isBase64Encoded($request->dockerfile)) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => [ + 'dockerfile' => 'The dockerfile should be base64 encoded.', + ], + ], 422); + } + $dockerFile = base64_decode($request->dockerfile); + if (mb_detect_encoding($dockerFile, 'ASCII', true) === false) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => [ + 'dockerfile' => 'The dockerfile should be base64 encoded.', + ], + ], 422); + } + $dockerFile = base64_decode($request->dockerfile); + $this->removeUnnecessaryFieldsFromRequest($request); + + $port = get_port_from_dockerfile($request->dockerfile); + if (! $port) { + $port = 80; + } + + $application = new Application(); + $application->fill($request->all()); + $application->fqdn = $fqdn; + $application->ports_exposes = $port; + $application->build_pack = 'dockerfile'; + $application->dockerfile = $dockerFile; + $application->destination_id = $destination->id; + $application->destination_type = $destination->getMorphClass(); + $application->environment_id = $environment->id; + + $application->git_repository = 'coollabsio/coolify'; + $application->git_branch = 'main'; + $application->save(); + + if ($instantDeploy) { + $deployment_uuid = new Cuid2(7); + + queue_application_deployment( + application: $application, + deployment_uuid: $deployment_uuid, + no_questions_asked: true, + is_api: true, + ); + } + return response()->json(serialize_api_response($application)); } @@ -826,6 +901,8 @@ private function removeUnnecessaryFieldsFromRequest(Request $request) private function validateDataApplications(Request $request, Server $server) { + $teamId = get_team_id_from_token(); + // Validate ports_mappings if ($request->has('ports_mappings')) { $ports = []; @@ -881,6 +958,14 @@ private function validateDataApplications(Request $request, Server $server) 'errors' => $errors, ], 422); } + if (checkIfDomainIsAlreadyUsed($fqdn, $teamId)) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => [ + 'domains' => 'One of the domain is already used.', + ], + ], 422); + } } } } diff --git a/app/Models/Server.php b/app/Models/Server.php index cd6cc9890..ea487fee7 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -502,7 +502,7 @@ public function checkSentinel() $sentinel_found = json_decode($sentinel_found, true); $status = data_get($sentinel_found, '0.State.Status', 'exited'); if ($status !== 'running') { - ray('Sentinel is not running, starting it...'); + // ray('Sentinel is not running, starting it...'); PullSentinelImageJob::dispatch($this); } else { // ray('Sentinel is running'); diff --git a/app/Models/ServiceApplication.php b/app/Models/ServiceApplication.php index 98c1cf4e7..6690f254e 100644 --- a/app/Models/ServiceApplication.php +++ b/app/Models/ServiceApplication.php @@ -27,6 +27,11 @@ public function restart() instant_remote_process(["docker restart {$container_id}"], $this->service->server); } + public static function ownedByCurrentTeamAPI(int $teamId) + { + return ServiceApplication::whereRelation('service.environment.project.team', 'id', $teamId)->orderBy('name'); + } + public function isLogDrainEnabled() { return data_get($this, 'is_log_drain_enabled', false); diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index a4676cfd4..1d70b674c 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -56,6 +56,8 @@ use Symfony\Component\Yaml\Yaml; use Visus\Cuid2\Cuid2; +use function PHPUnit\Framework\isEmpty; + function base_configuration_dir(): string { return '/data/coolify'; @@ -2129,6 +2131,75 @@ function ip_match($ip, $cidrs, &$match = null) return false; } +function checkIfDomainIsAlreadyUsed(Collection|array $domains, ?string $teamId = null) +{ + if (is_null($teamId)) { + return response()->json(['error' => 'Team ID is required.'], 400); + } + if (is_array($domains)) { + $domains = collect($domains); + } + + $domains = $domains->map(function ($domain) { + if (str($domain)->endsWith('/')) { + $domain = str($domain)->beforeLast('/'); + } + + return str($domain); + }); + $applications = Application::ownedByCurrentTeamAPI($teamId)->get('fqdn'); + $serviceApplications = ServiceApplication::ownedByCurrentTeamAPI($teamId)->get('fqdn'); + $domainFound = false; + foreach ($applications as $app) { + if (is_null($app->fqdn)) { + continue; + } + $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); + foreach ($list_of_domains as $domain) { + if (str($domain)->endsWith('/')) { + $domain = str($domain)->beforeLast('/'); + } + $naked_domain = str($domain)->value(); + if ($domains->contains($naked_domain)) { + $domainFound = true; + break; + } + } + } + if ($domainFound) { + return true; + } + foreach ($serviceApplications as $app) { + if (isEmpty($app->fqdn)) { + continue; + } + $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); + foreach ($list_of_domains as $domain) { + if (str($domain)->endsWith('/')) { + $domain = str($domain)->beforeLast('/'); + } + $naked_domain = str($domain)->value(); + if ($domains->contains($naked_domain)) { + $domainFound = true; + break; + } + } + } + if ($domainFound) { + return true; + } + $settings = InstanceSettings::get(); + if (data_get($settings, 'fqdn')) { + $domain = data_get($settings, 'fqdn'); + if (str($domain)->endsWith('/')) { + $domain = str($domain)->beforeLast('/'); + } + $naked_domain = str($domain)->value(); + if ($domains->contains($naked_domain)) { + return true; + } + } +} function check_domain_usage(ServiceApplication|Application|null $resource = null, ?string $domain = null) { if ($resource) { From da6f2da3d059d46e3698e8081fe7834f8bd455c4 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 1 Jul 2024 16:26:50 +0200 Subject: [PATCH 24/54] feat: lots of api endpoints --- app/Enums/NewDatabaseTypes.php | 15 + app/Events/DatabaseStatusChanged.php | 2 +- ...cations.php => ApplicationsController.php} | 408 +++++++++++++----- .../Controllers/Api/DatabasesController.php | 259 +++++++++++ .../Api/{Deploy.php => DeployController.php} | 58 ++- ...php => EnvironmentVariablesController.php} | 7 +- app/Http/Controllers/Api/Project.php | 44 -- .../Controllers/Api/ProjectController.php | 60 +++ ...{Resources.php => ResourcesController.php} | 11 +- .../Controllers/Api/SecurityController.php | 160 +++++++ .../{Servers.php => ServersController.php} | 52 ++- app/Http/Controllers/Api/Team.php | 74 ---- app/Http/Controllers/Api/TeamController.php | 89 ++++ app/Http/Middleware/ApiAllowed.php | 34 ++ app/Livewire/Settings/Configuration.php | 21 +- app/Models/InstanceSettings.php | 1 + app/Models/StandaloneClickhouse.php | 35 +- app/Models/StandaloneDragonfly.php | 35 +- app/Models/StandaloneKeydb.php | 35 +- app/Models/StandaloneMariadb.php | 35 +- app/Models/StandaloneMongodb.php | 33 +- app/Models/StandaloneMysql.php | 35 +- app/Models/StandalonePostgresql.php | 35 +- app/Models/StandaloneRedis.php | 35 +- bootstrap/helpers/api.php | 53 ++- bootstrap/helpers/databases.php | 168 +++++--- bootstrap/helpers/shared.php | 37 ++ ...1_115528_add_is_api_allowed_and_iplist.php | 24 ++ .../livewire/settings/configuration.blade.php | 17 +- routes/api.php | 128 ++++-- 30 files changed, 1583 insertions(+), 417 deletions(-) create mode 100644 app/Enums/NewDatabaseTypes.php rename app/Http/Controllers/Api/{Applications.php => ApplicationsController.php} (72%) create mode 100644 app/Http/Controllers/Api/DatabasesController.php rename app/Http/Controllers/Api/{Deploy.php => DeployController.php} (73%) rename app/Http/Controllers/Api/{EnvironmentVariables.php => EnvironmentVariablesController.php} (86%) delete mode 100644 app/Http/Controllers/Api/Project.php create mode 100644 app/Http/Controllers/Api/ProjectController.php rename app/Http/Controllers/Api/{Resources.php => ResourcesController.php} (80%) create mode 100644 app/Http/Controllers/Api/SecurityController.php rename app/Http/Controllers/Api/{Servers.php => ServersController.php} (79%) delete mode 100644 app/Http/Controllers/Api/Team.php create mode 100644 app/Http/Controllers/Api/TeamController.php create mode 100644 app/Http/Middleware/ApiAllowed.php create mode 100644 database/migrations/2024_07_01_115528_add_is_api_allowed_and_iplist.php diff --git a/app/Enums/NewDatabaseTypes.php b/app/Enums/NewDatabaseTypes.php new file mode 100644 index 000000000..3563146ff --- /dev/null +++ b/app/Enums/NewDatabaseTypes.php @@ -0,0 +1,15 @@ +user()->id ?? null; } if (is_null($userId)) { - throw new \Exception('User id is null'); + throw new \RuntimeException('User id is null'); } $this->userId = $userId; } diff --git a/app/Http/Controllers/Api/Applications.php b/app/Http/Controllers/Api/ApplicationsController.php similarity index 72% rename from app/Http/Controllers/Api/Applications.php rename to app/Http/Controllers/Api/ApplicationsController.php index c73b894ba..e37de0378 100644 --- a/app/Http/Controllers/Api/Applications.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -13,47 +13,45 @@ use App\Models\PrivateKey; use App\Models\Project; use App\Models\Server; +use App\Models\Service; use Illuminate\Http\Request; use Illuminate\Validation\Rule; +use Symfony\Component\Yaml\Yaml; use Visus\Cuid2\Cuid2; -class Applications extends Controller +class ApplicationsController extends Controller { public function applications(Request $request) { - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } $projects = Project::where('team_id', $teamId)->get(); $applications = collect(); $applications->push($projects->pluck('applications')->flatten()); $applications = $applications->flatten(); + $applications = $applications->map(function ($application) { + return serializeApiResponse($application); + }); - return response()->json(serialize_api_response($applications)); + return response()->json([ + 'success' => true, + 'data' => $applications, + ]); } public function create_application(Request $request) { - - ray()->clearAll(); $allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile']; - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } - if (! $request->isJson()) { - return response()->json([ - 'message' => 'Invalid request.', - 'error' => 'Content-Type must be application/json.', - ], 400); - } - // check if request is valid json - if (! json_decode($request->getContent())) { - return response()->json([ - 'message' => 'Invalid request.', - 'error' => 'Invalid JSON.', - ], 400); + + $return = validateIncomingRequest($request); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; } $validator = customApiValidator($request->all(), [ 'name' => 'string|max:255', @@ -75,6 +73,7 @@ public function create_application(Request $request) } return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => $errors, ], 422); @@ -88,22 +87,22 @@ public function create_application(Request $request) $project = Project::whereTeamId($teamId)->whereUuid($request->project_uuid)->first(); if (! $project) { - return response()->json(['error' => 'Project not found.'], 404); + return response()->json(['succes' => false, 'message' => 'Project not found.'], 404); } $environment = $project->environments()->where('name', $request->environment_name)->first(); if (! $environment) { - return response()->json(['error' => 'Environment not found.'], 404); + return response()->json(['success' => false, 'message' => 'Environment not found.'], 404); } $server = Server::whereTeamId($teamId)->whereUuid($serverUuid)->first(); if (! $server) { - return response()->json(['error' => 'Server not found.'], 404); + return response()->json(['success' => false, 'message' => 'Server not found.'], 404); } $destinations = $server->destinations(); if ($destinations->count() == 0) { - return response()->json(['error' => 'Server has no destinations.'], 400); + return response()->json(['success' => false, 'message' => 'Server has no destinations.'], 400); } if ($destinations->count() > 1 && ! $request->has('destination_uuid')) { - return response()->json(['error' => 'Server has multiple destinations and you do not set destination_uuid.'], 400); + return response()->json(['success' => false, 'message' => 'Server has multiple destinations and you do not set destination_uuid.'], 400); } $destination = $destinations->first(); if ($type === 'public') { @@ -114,11 +113,12 @@ public function create_application(Request $request) sharedDataApplications(), 'git_repository' => 'string|required', 'git_branch' => 'string|required', - 'build_pack' => ['required', Rule::enum(BuildPackTypes::class)], + 'build_pack' => [Rule::enum(BuildPackTypes::class)], 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required', ]); if ($validator->fails()) { return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => $validator->errors(), ], 422); @@ -128,7 +128,7 @@ public function create_application(Request $request) return $return; } $application = new Application(); - $this->removeUnnecessaryFieldsFromRequest($request); + removeUnnecessaryFieldsFromRequest($request); $application->fill($request->all()); @@ -149,7 +149,10 @@ public function create_application(Request $request) ); } - return response()->json(serialize_api_response($application)); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($application), + ]); } elseif ($type === 'private-gh-app') { if (! $request->has('name')) { $request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch)); @@ -164,6 +167,7 @@ public function create_application(Request $request) ]); if ($validator->fails()) { return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => $validator->errors(), ], 422); @@ -174,14 +178,14 @@ public function create_application(Request $request) } $githubApp = GithubApp::whereTeamId($teamId)->where('uuid', $githubAppUuid)->first(); if (! $githubApp) { - return response()->json(['error' => 'Github App not found.'], 404); + return response()->json(['success' => false, 'message' => 'Github App not found.'], 404); } $gitRepository = $request->git_repository; if (str($gitRepository)->startsWith('http') || str($gitRepository)->contains('github.com')) { $gitRepository = str($gitRepository)->replace('https://', '')->replace('http://', '')->replace('github.com/', ''); } $application = new Application(); - $this->removeUnnecessaryFieldsFromRequest($request); + removeUnnecessaryFieldsFromRequest($request); $application->fill($request->all()); @@ -205,7 +209,10 @@ public function create_application(Request $request) ); } - return response()->json(serialize_api_response($application)); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($application), + ]); } elseif ($type === 'private-deploy-key') { if (! $request->has('name')) { $request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch)); @@ -230,11 +237,11 @@ public function create_application(Request $request) } $privateKey = PrivateKey::whereTeamId($teamId)->where('uuid', $request->private_key_uuid)->first(); if (! $privateKey) { - return response()->json(['error' => 'Private Key not found.'], 404); + return response()->json(['success' => false, 'message' => 'Private Key not found.'], 404); } $application = new Application(); - $this->removeUnnecessaryFieldsFromRequest($request); + removeUnnecessaryFieldsFromRequest($request); $application->fill($request->all()); $application->fqdn = $fqdn; @@ -255,7 +262,10 @@ public function create_application(Request $request) ); } - return response()->json(serialize_api_response($application)); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($application), + ]); } elseif ($type === 'dockerfile') { if (! $request->has('name')) { $request->offsetSet('name', 'dockerfile-'.new Cuid2(7)); @@ -266,6 +276,7 @@ public function create_application(Request $request) ]); if ($validator->fails()) { return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => $validator->errors(), ], 422); @@ -276,6 +287,7 @@ public function create_application(Request $request) } if (! isBase64Encoded($request->dockerfile)) { return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => [ 'dockerfile' => 'The dockerfile should be base64 encoded.', @@ -285,6 +297,7 @@ public function create_application(Request $request) $dockerFile = base64_decode($request->dockerfile); if (mb_detect_encoding($dockerFile, 'ASCII', true) === false) { return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => [ 'dockerfile' => 'The dockerfile should be base64 encoded.', @@ -292,7 +305,7 @@ public function create_application(Request $request) ], 422); } $dockerFile = base64_decode($request->dockerfile); - $this->removeUnnecessaryFieldsFromRequest($request); + removeUnnecessaryFieldsFromRequest($request); $port = get_port_from_dockerfile($request->dockerfile); if (! $port) { @@ -324,42 +337,179 @@ public function create_application(Request $request) ); } - return response()->json(serialize_api_response($application)); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($application), + ]); + } elseif ($type === 'docker-image') { + if (! $request->has('name')) { + $request->offsetSet('name', 'docker-image-'.new Cuid2(7)); + } + $validator = customApiValidator($request->all(), [ + sharedDataApplications(), + 'docker_registry_image_name' => 'string|required', + 'docker_registry_image_tag' => 'string', + 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required', + ]); + if ($validator->fails()) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => $validator->errors(), + ], 422); + } + $return = $this->validateDataApplications($request, $server); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } + if (! $request->docker_registry_image_tag) { + $request->offsetSet('docker_registry_image_tag', 'latest'); + } + $application = new Application(); + removeUnnecessaryFieldsFromRequest($request); + + $application->fill($request->all()); + $application->fqdn = $fqdn; + $application->build_pack = 'dockerimage'; + $application->destination_id = $destination->id; + $application->destination_type = $destination->getMorphClass(); + $application->environment_id = $environment->id; + + $application->git_repository = 'coollabsio/coolify'; + $application->git_branch = 'main'; + $application->save(); + + if ($instantDeploy) { + $deployment_uuid = new Cuid2(7); + + queue_application_deployment( + application: $application, + deployment_uuid: $deployment_uuid, + no_questions_asked: true, + is_api: true, + ); + } + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($application), + ]); + } elseif ($type === 'docker-compose-empty') { + if (! $request->has('name')) { + $request->offsetSet('name', 'service'.new Cuid2(7)); + } + $validator = customApiValidator($request->all(), [ + sharedDataApplications(), + 'docker_compose' => 'string|required', + 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required', + ]); + if ($validator->fails()) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => $validator->errors(), + ], 422); + } + $return = $this->validateDataApplications($request, $server); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } + if (! isBase64Encoded($request->docker_compose)) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => [ + 'docker_compose' => 'The docker_compose should be base64 encoded.', + ], + ], 422); + } + $dockerCompose = base64_decode($request->docker_compose); + if (mb_detect_encoding($dockerCompose, 'ASCII', true) === false) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => [ + 'docker_compose' => 'The docker_compose should be base64 encoded.', + ], + ], 422); + } + $dockerCompose = base64_decode($request->docker_compose); + $dockerComposeRaw = Yaml::dump(Yaml::parse($dockerCompose), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK); + + // $isValid = validateComposeFile($dockerComposeRaw, $server_id); + // if ($isValid !== 'OK') { + // return $this->dispatch('error', "Invalid docker-compose file.\n$isValid"); + // } + + $service = new Service(); + removeUnnecessaryFieldsFromRequest($request); + $service->name = $request->name; + $service->description = $request->description; + $service->docker_compose_raw = $dockerComposeRaw; + $service->environment_id = $environment->id; + $service->server_id = $server->id; + $service->destination_id = $destination->id; + $service->destination_type = $destination->getMorphClass(); + $service->save(); + + $service->name = "service-$service->uuid"; + $service->parse(isNew: true); + // if ($instantDeploy) { + // $deployment_uuid = new Cuid2(7); + + // queue_application_deployment( + // application: $application, + // deployment_uuid: $deployment_uuid, + // no_questions_asked: true, + // is_api: true, + // ); + // } + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($service), + ]); } - return response()->json(['error' => 'Invalid type.'], 400); + return response()->json(['success' => false, 'message' => 'Invalid type.'], 400); } public function application_by_uuid(Request $request) { - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } $uuid = $request->route('uuid'); if (! $uuid) { - return response()->json(['error' => 'UUID is required.'], 400); + return response()->json(['success' => false, 'message' => 'UUID is required.'], 400); + } + $return = validateIncomingRequest($request); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; } $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); if (! $application) { - return response()->json(['error' => 'Application not found.'], 404); + return response()->json(['success' => false, 'message' => 'Application not found.'], 404); } - return response()->json(serialize_api_response($application)); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($application), + ]); } public function delete_by_uuid(Request $request) { - ray()->clearAll(); - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); $cleanup = $request->query->get('cleanup') ?? false; if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } if ($request->collect()->count() == 0) { return response()->json([ + 'success' => false, 'message' => 'Invalid request.', ], 400); } @@ -381,17 +531,21 @@ public function delete_by_uuid(Request $request) public function update_by_uuid(Request $request) { - ray()->clearAll(); - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } if ($request->collect()->count() == 0) { return response()->json([ + 'success' => false, 'message' => 'Invalid request.', ], 400); } + $return = validateIncomingRequest($request); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); if (! $application) { @@ -423,6 +577,7 @@ public function update_by_uuid(Request $request) foreach ($ports as $port) { if (! is_numeric($port)) { return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => [ 'ports_exposes' => 'The ports_exposes should be a comma separated list of numbers.', @@ -445,6 +600,7 @@ public function update_by_uuid(Request $request) } return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => $errors, ], 422); @@ -467,15 +623,21 @@ public function update_by_uuid(Request $request) $application->fill($data); $application->save(); - return response()->json(serialize_api_response($application)); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($application), + ]); } public function envs_by_uuid(Request $request) { - ray()->clearAll(); - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); + } + $return = validateIncomingRequest($request); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; } $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); @@ -487,17 +649,24 @@ public function envs_by_uuid(Request $request) } $envs = $application->environment_variables->sortBy('id')->merge($application->environment_variables_preview->sortBy('id')); - return response()->json(serialize_api_response($envs)); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($envs), + ]); } public function update_env_by_uuid(Request $request) { - ray()->clearAll(); $allowedFields = ['key', 'value', 'is_preview', 'is_build_time', 'is_literal']; - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); + } + + $return = validateIncomingRequest($request); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; } $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); @@ -525,6 +694,7 @@ public function update_env_by_uuid(Request $request) } return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => $errors, ], 422); @@ -547,9 +717,10 @@ public function update_env_by_uuid(Request $request) } $env->save(); - return response()->json(serialize_api_response($env)); + return response()->json(serializeApiResponse($env)); } else { return response()->json([ + 'success' => false, 'message' => 'Environment variable not found.', ], 404); } @@ -568,10 +739,14 @@ public function update_env_by_uuid(Request $request) } $env->save(); - return response()->json(serialize_api_response($env)); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($env), + ]); } else { return response()->json([ + 'success' => false, 'message' => 'Environment variable not found.', ], 404); @@ -579,6 +754,7 @@ public function update_env_by_uuid(Request $request) } return response()->json([ + 'success' => false, 'message' => 'Something went wrong.', ], 500); @@ -586,11 +762,15 @@ public function update_env_by_uuid(Request $request) public function create_bulk_envs(Request $request) { - ray()->clearAll(); - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); + } + + $return = validateIncomingRequest($request); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; } $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); @@ -604,6 +784,7 @@ public function create_bulk_envs(Request $request) $bulk_data = $request->get('data'); if (! $bulk_data) { return response()->json([ + 'success' => false, 'message' => 'Bulk data is required.', ], 400); } @@ -620,6 +801,7 @@ public function create_bulk_envs(Request $request) ]); if ($validator->fails()) { return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => $validator->errors(), ], 422); @@ -671,18 +853,18 @@ public function create_bulk_envs(Request $request) } return response()->json([ - 'message' => 'Environments updated.', + 'success' => true, + 'data' => serializeApiResponse($env), ]); } public function create_env(Request $request) { - ray()->clearAll(); $allowedFields = ['key', 'value', 'is_preview', 'is_build_time', 'is_literal']; - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); @@ -710,6 +892,7 @@ public function create_env(Request $request) } return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => $errors, ], 422); @@ -719,6 +902,7 @@ public function create_env(Request $request) $env = $application->environment_variables_preview->where('key', $request->key)->first(); if ($env) { return response()->json([ + 'success' => false, 'message' => 'Environment variable already exists. Use PATCH request to update it.', ], 409); } else { @@ -730,7 +914,10 @@ public function create_env(Request $request) 'is_literal' => $request->is_literal ?? false, ]); - return response()->json(serialize_api_response($env))->setStatusCode(201); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($env), + ])->setStatusCode(201); } } else { $env = $application->environment_variables->where('key', $request->key)->first(); @@ -747,12 +934,16 @@ public function create_env(Request $request) 'is_literal' => $request->is_literal ?? false, ]); - return response()->json(serialize_api_response($env))->setStatusCode(201); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($env), + ])->setStatusCode(201); } } return response()->json([ + 'success' => false, 'message' => 'Something went wrong.', ], 500); @@ -760,10 +951,9 @@ public function create_env(Request $request) public function delete_env_by_uuid(Request $request) { - ray()->clearAll(); - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); @@ -790,19 +980,19 @@ public function delete_env_by_uuid(Request $request) public function action_deploy(Request $request) { - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } $force = $request->query->get('force') ?? false; $instant_deploy = $request->query->get('instant_deploy') ?? false; $uuid = $request->route('uuid'); if (! $uuid) { - return response()->json(['error' => 'UUID is required.'], 400); + return response()->json(['success' => false, 'message' => 'UUID is required.'], 400); } $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); if (! $application) { - return response()->json(['error' => 'Application not found.'], 404); + return response()->json(['success' => false, 'message' => 'Application not found.'], 404); } $deployment_uuid = new Cuid2(7); @@ -817,9 +1007,12 @@ public function action_deploy(Request $request) return response()->json( [ + 'success' => true, 'message' => 'Deployment request queued.', - 'deployment_uuid' => $deployment_uuid->toString(), - 'deployment_api_url' => base_url().'/api/v1/deployment/'.$deployment_uuid->toString(), + 'data' => [ + 'deployment_uuid' => $deployment_uuid->toString(), + 'deployment_api_url' => base_url().'/api/v1/deployment/'.$deployment_uuid->toString(), + ], ], 200 ); @@ -827,43 +1020,53 @@ public function action_deploy(Request $request) public function action_stop(Request $request) { - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } $uuid = $request->route('uuid'); $sync = $request->query->get('sync') ?? false; if (! $uuid) { - return response()->json(['error' => 'UUID is required.'], 400); + return response()->json(['success' => false, 'message' => 'UUID is required.'], 400); } $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); if (! $application) { - return response()->json(['error' => 'Application not found.'], 404); + return response()->json(['success' => false, 'message' => 'Application not found.'], 404); } if ($sync) { StopApplication::run($application); - return response()->json(['message' => 'Stopped the application.'], 200); + return response()->json( + [ + 'success' => true, + 'message' => 'Stopped the application.', + ], + ); } else { StopApplication::dispatch($application); - return response()->json(['message' => 'Stopping request queued.'], 200); + return response()->json( + [ + 'success' => true, + 'message' => 'Stopping request queued.', + ], + ); } } public function action_restart(Request $request) { - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } $uuid = $request->route('uuid'); if (! $uuid) { - return response()->json(['error' => 'UUID is required.'], 400); + return response()->json(['success' => false, 'message' => 'UUID is required.'], 400); } $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); if (! $application) { - return response()->json(['error' => 'Application not found.'], 404); + return response()->json(['success' => false, 'message' => 'Application not found.'], 404); } $deployment_uuid = new Cuid2(7); @@ -877,31 +1080,25 @@ public function action_restart(Request $request) return response()->json( [ + 'success' => true, 'message' => 'Restart request queued.', - 'deployment_uuid' => $deployment_uuid->toString(), - 'deployment_api_url' => base_url().'/api/v1/deployment/'.$deployment_uuid->toString(), + 'data' => [ + 'deployment_uuid' => $deployment_uuid->toString(), + 'deployment_api_url' => base_url().'/api/v1/deployment/'.$deployment_uuid->toString(), + ], ], - 200 ); } - private function removeUnnecessaryFieldsFromRequest(Request $request) - { - $request->offsetUnset('project_uuid'); - $request->offsetUnset('environment_name'); - $request->offsetUnset('destination_uuid'); - $request->offsetUnset('server_uuid'); - $request->offsetUnset('type'); - $request->offsetUnset('domains'); - $request->offsetUnset('instant_deploy'); - $request->offsetUnset('github_app_uuid'); - $request->offsetUnset('private_key_uuid'); - } - private function validateDataApplications(Request $request, Server $server) { - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); + + // Default build pack is nixpacks + if (! $request->has('build_pack')) { + $request->offsetSet('build_pack', 'nixpacks'); + } // Validate ports_mappings if ($request->has('ports_mappings')) { @@ -910,6 +1107,7 @@ private function validateDataApplications(Request $request, Server $server) $port = explode(':', $portMapping); if (in_array($port[0], $ports)) { return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => [ 'ports_mappings' => 'The first number before : should be unique between mappings.', @@ -923,6 +1121,7 @@ private function validateDataApplications(Request $request, Server $server) if ($request->has('custom_labels')) { if (! isBase64Encoded($request->custom_labels)) { return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => [ 'custom_labels' => 'The custom_labels should be base64 encoded.', @@ -932,6 +1131,7 @@ private function validateDataApplications(Request $request, Server $server) $customLabels = base64_decode($request->custom_labels); if (mb_detect_encoding($customLabels, 'ASCII', true) === false) { return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => [ 'custom_labels' => 'The custom_labels should be base64 encoded.', @@ -954,12 +1154,14 @@ private function validateDataApplications(Request $request, Server $server) }); if (count($errors) > 0) { return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => $errors, ], 422); } if (checkIfDomainIsAlreadyUsed($fqdn, $teamId)) { return response()->json([ + 'success' => false, 'message' => 'Validation failed.', 'errors' => [ 'domains' => 'One of the domain is already used.', diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php new file mode 100644 index 000000000..36a5fffaf --- /dev/null +++ b/app/Http/Controllers/Api/DatabasesController.php @@ -0,0 +1,259 @@ +get(); + $databases = collect(); + foreach ($projects as $project) { + $databases = $databases->merge($project->databases()); + } + $databases = $databases->map(function ($database) { + return serializeApiResponse($database); + }); + + return response()->json([ + 'success' => true, + 'data' => $databases, + ]); + } + + public function database_by_uuid(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + if (! $request->uuid) { + return response()->json(['success' => false, 'message' => 'UUID is required.'], 404); + } + $database = queryDatabaseByUuidWithinTeam($request->uuid, $teamId); + if (! $database) { + return response()->json(['success' => false, 'message' => 'Database not found.'], 404); + } + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($database), + ]); + } + + public function create_database(Request $request) + { + $allowedFields = ['type', 'name', 'description', 'image', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'postgres_user', 'postgres_password', 'postgres_db', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares']; + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + $return = validateIncomingRequest($request); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } + $validator = customApiValidator($request->all(), [ + 'type' => ['required', Rule::enum(NewDatabaseTypes::class)], + 'name' => 'string|max:255', + 'description' => 'string|nullable', + 'image' => 'string', + 'project_uuid' => 'string|required', + 'environment_name' => 'string|required', + 'server_uuid' => 'string|required', + 'destination_uuid' => 'string', + 'postgres_user' => 'string', + 'postgres_password' => 'string', + 'postgres_db' => 'string', + 'limits_memory' => 'string', + 'limits_memory_swap' => 'string', + 'limits_memory_swappiness' => 'numeric', + 'limits_memory_reservation' => 'string', + 'limits_cpus' => 'string', + 'limits_cpuset' => 'string|nullable', + 'limits_cpu_shares' => 'numeric', + 'instant_deploy' => 'boolean', + ]); + + $extraFields = array_diff(array_keys($request->all()), $allowedFields); + if ($validator->fails() || ! empty($extraFields)) { + $errors = $validator->errors(); + if (! empty($extraFields)) { + foreach ($extraFields as $field) { + $errors->add($field, 'This field is not allowed.'); + } + } + + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + $serverUuid = $request->server_uuid; + $instantDeploy = $request->instant_deploy ?? false; + + $project = Project::whereTeamId($teamId)->whereUuid($request->project_uuid)->first(); + if (! $project) { + return response()->json(['succes' => false, 'message' => 'Project not found.'], 404); + } + $environment = $project->environments()->where('name', $request->environment_name)->first(); + if (! $environment) { + return response()->json(['success' => false, 'message' => 'Environment not found.'], 404); + } + $server = Server::whereTeamId($teamId)->whereUuid($serverUuid)->first(); + if (! $server) { + return response()->json(['success' => false, 'message' => 'Server not found.'], 404); + } + $destinations = $server->destinations(); + if ($destinations->count() == 0) { + return response()->json(['success' => false, 'message' => 'Server has no destinations.'], 400); + } + if ($destinations->count() > 1 && ! $request->has('destination_uuid')) { + return response()->json(['success' => false, 'message' => 'Server has multiple destinations and you do not set destination_uuid.'], 400); + } + $destination = $destinations->first(); + + if ($request->type === NewDatabaseTypes::POSTGRESQL->value) { + removeUnnecessaryFieldsFromRequest($request); + $database = create_standalone_postgresql($environment->id, $destination->uuid, $request->all()); + if ($instantDeploy) { + StartPostgresql::dispatch($database); + } + + return response()->json([ + 'success' => true, + 'message' => 'Database starting queued.', + 'data' => serializeApiResponse($database), + ]); + } elseif ($request->type === NewDatabaseTypes::MARIADB->value) { + removeUnnecessaryFieldsFromRequest($request); + $database = create_standalone_mariadb($environment->id, $destination->uuid, $request->all()); + if ($instantDeploy) { + StartMariadb::dispatch($database); + } + + return response()->json([ + 'success' => true, + 'message' => 'Database starting queued.', + 'data' => serializeApiResponse($database), + ]); + } elseif ($request->type === NewDatabaseTypes::MYSQL->value) { + removeUnnecessaryFieldsFromRequest($request); + $database = create_standalone_mysql($environment->id, $destination->uuid, $request->all()); + if ($instantDeploy) { + StartMysql::dispatch($database); + } + + return response()->json([ + 'success' => true, + 'message' => 'Database starting queued.', + 'data' => serializeApiResponse($database), + ]); + } elseif ($request->type === NewDatabaseTypes::REDIS->value) { + removeUnnecessaryFieldsFromRequest($request); + $database = create_standalone_redis($environment->id, $destination->uuid, $request->all()); + if ($instantDeploy) { + StartRedis::dispatch($database); + } + + return response()->json([ + 'success' => true, + 'message' => 'Database starting queued.', + 'data' => serializeApiResponse($database), + ]); + } elseif ($request->type === NewDatabaseTypes::DRAGONFLY->value) { + removeUnnecessaryFieldsFromRequest($request); + $database = create_standalone_dragonfly($environment->id, $destination->uuid, $request->all()); + if ($instantDeploy) { + StartDragonfly::dispatch($database); + } + + return response()->json([ + 'success' => true, + 'message' => 'Database starting queued.', + 'data' => serializeApiResponse($database), + ]); + } elseif ($request->type === NewDatabaseTypes::KEYDB->value) { + removeUnnecessaryFieldsFromRequest($request); + $database = create_standalone_keydb($environment->id, $destination->uuid, $request->all()); + if ($instantDeploy) { + StartKeydb::dispatch($database); + } + + return response()->json([ + 'success' => true, + 'message' => 'Database starting queued.', + 'data' => serializeApiResponse($database), + ]); + } elseif ($request->type === NewDatabaseTypes::CLICKHOUSE->value) { + removeUnnecessaryFieldsFromRequest($request); + $database = create_standalone_clickhouse($environment->id, $destination->uuid, $request->all()); + if ($instantDeploy) { + StartClickhouse::dispatch($database); + } + + return response()->json([ + 'success' => true, + 'message' => 'Database starting queued.', + 'data' => serializeApiResponse($database), + ]); + } elseif ($request->type === NewDatabaseTypes::MONGODB->value) { + removeUnnecessaryFieldsFromRequest($request); + $database = create_standalone_mongodb($environment->id, $destination->uuid, $request->all()); + if ($instantDeploy) { + StartMongodb::dispatch($database); + } + + return response()->json([ + 'success' => true, + 'message' => 'Database starting queued.', + 'data' => serializeApiResponse($database), + ]); + } + + return response()->json(['success' => false, 'message' => 'Invalid database type requested.'], 400); + } + + public function delete_by_uuid(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + if (! $request->uuid) { + return response()->json(['success' => false, 'message' => 'UUID is required.'], 404); + } + $database = queryDatabaseByUuidWithinTeam($request->uuid, $teamId); + if (! $database) { + return response()->json(['success' => false, 'message' => 'Database not found.'], 404); + } + StopDatabase::dispatch($database); + $database->delete(); + + return response()->json([ + 'success' => true, + 'message' => 'Database deletion request queued.', + ]); + } +} diff --git a/app/Http/Controllers/Api/Deploy.php b/app/Http/Controllers/Api/DeployController.php similarity index 73% rename from app/Http/Controllers/Api/Deploy.php rename to app/Http/Controllers/Api/DeployController.php index d510970dd..76e67548c 100644 --- a/app/Http/Controllers/Api/Deploy.php +++ b/app/Http/Controllers/Api/DeployController.php @@ -18,13 +18,13 @@ use Illuminate\Http\Request; use Visus\Cuid2\Cuid2; -class Deploy extends Controller +class DeployController extends Controller { public function deployments(Request $request) { - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } $servers = Server::whereTeamId($teamId)->get(); $deployments_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('server_id', $servers->pluck('id'))->get([ @@ -38,39 +38,45 @@ public function deployments(Request $request) 'status', ])->sortBy('id')->toArray(); - return response()->json(serialize_api_response($deployments_per_server), 200); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($deployments_per_server), + ]); } public function deployment_by_uuid(Request $request) { - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } $uuid = $request->route('uuid'); if (! $uuid) { - return response()->json(['message' => 'UUID is required.'], 400); + return response()->json(['success' => false, 'message' => 'UUID is required.'], 400); } $deployment = ApplicationDeploymentQueue::where('deployment_uuid', $uuid)->first(); if (! $deployment) { - return response()->json(['message' => 'Deployment not found.'], 404); + return response()->json(['success' => false, 'message' => 'Deployment not found.'], 404); } - return response()->json(serialize_api_response($deployment->makeHidden('logs')), 200); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($deployment->makeHidden('logs')), + ]); } public function deploy(Request $request) { - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); $uuids = $request->query->get('uuid'); $tags = $request->query->get('tag'); $force = $request->query->get('force') ?? false; if ($uuids && $tags) { - return response()->json(['message' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); + return response()->json(['success' => false, 'message' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); } if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } if ($tags) { return $this->by_tags($tags, $teamId, $force); @@ -78,7 +84,7 @@ public function deploy(Request $request) return $this->by_uuids($uuids, $teamId, $force); } - return response()->json(['message' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); + return response()->json(['success' => false, 'message' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); } private function by_uuids(string $uuid, int $teamId, bool $force = false) @@ -87,7 +93,7 @@ private function by_uuids(string $uuid, int $teamId, bool $force = false) $uuids = collect(array_filter($uuids)); if (count($uuids) === 0) { - return response()->json(['message' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); + return response()->json(['success' => false, 'message' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); } $deployments = collect(); $payload = collect(); @@ -96,19 +102,22 @@ private function by_uuids(string $uuid, int $teamId, bool $force = false) if ($resource) { ['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force); if ($deployment_uuid) { - $deployments->push(['message' => $return_message, 'resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]); + $deployments->push(['success' => true, 'message' => $return_message, 'resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]); } else { - $deployments->push(['message' => $return_message, 'resource_uuid' => $uuid]); + $deployments->push(['success' => true, 'message' => $return_message, 'resource_uuid' => $uuid]); } } } if ($deployments->count() > 0) { $payload->put('deployments', $deployments->toArray()); - return response()->json($payload->toArray(), 200); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($payload->toArray()), + ]); } - return response()->json(['message' => 'No resources found.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404); + return response()->json(['success' => false, 'message' => 'No resources found.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404); } public function by_tags(string $tags, int $team_id, bool $force = false) @@ -117,7 +126,7 @@ public function by_tags(string $tags, int $team_id, bool $force = false) $tags = collect(array_filter($tags)); if (count($tags) === 0) { - return response()->json(['message' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); + return response()->json(['success' => false, 'message' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); } $message = collect([]); $deployments = collect(); @@ -153,10 +162,13 @@ public function by_tags(string $tags, int $team_id, bool $force = false) $payload->put('details', $deployments->toArray()); } - return response()->json($payload->toArray(), 200); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($payload->toArray()), + ]); } - return response()->json(['message' => 'No resources found with this tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404); + return response()->json(['success' => false, 'message' => 'No resources found with this tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404); } public function deploy_resource($resource, bool $force = false): array @@ -164,7 +176,7 @@ public function deploy_resource($resource, bool $force = false): array $message = null; $deployment_uuid = null; if (gettype($resource) !== 'object') { - return ['message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid]; + return ['success' => false, 'message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid]; } $type = $resource?->getMorphClass(); if ($type === 'App\Models\Application') { @@ -228,6 +240,6 @@ public function deploy_resource($resource, bool $force = false): array $message = "Service {$resource->name} started. It could take a while, be patient."; } - return ['message' => $message, 'deployment_uuid' => $deployment_uuid]; + return ['success' => true, 'message' => $message, 'deployment_uuid' => $deployment_uuid]; } } diff --git a/app/Http/Controllers/Api/EnvironmentVariables.php b/app/Http/Controllers/Api/EnvironmentVariablesController.php similarity index 86% rename from app/Http/Controllers/Api/EnvironmentVariables.php rename to app/Http/Controllers/Api/EnvironmentVariablesController.php index d788bdb0c..c54656dc6 100644 --- a/app/Http/Controllers/Api/EnvironmentVariables.php +++ b/app/Http/Controllers/Api/EnvironmentVariablesController.php @@ -6,14 +6,13 @@ use App\Models\EnvironmentVariable; use Illuminate\Http\Request; -class EnvironmentVariables extends Controller +class EnvironmentVariablesController extends Controller { public function delete_env_by_uuid(Request $request) { - ray()->clearAll(); - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } $env = EnvironmentVariable::where('uuid', $request->env_uuid)->first(); if (! $env) { diff --git a/app/Http/Controllers/Api/Project.php b/app/Http/Controllers/Api/Project.php deleted file mode 100644 index baaf1eacb..000000000 --- a/app/Http/Controllers/Api/Project.php +++ /dev/null @@ -1,44 +0,0 @@ -select('id', 'name', 'uuid')->get(); - - return response()->json($projects); - } - - public function project_by_uuid(Request $request) - { - $teamId = get_team_id_from_token(); - if (is_null($teamId)) { - return invalid_token(); - } - $project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first()->load(['environments']); - - return response()->json($project); - } - - public function environment_details(Request $request) - { - $teamId = get_team_id_from_token(); - if (is_null($teamId)) { - return invalid_token(); - } - $project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first(); - $environment = $project->environments()->whereName(request()->environment_name)->first()->load(['applications', 'postgresqls', 'redis', 'mongodbs', 'mysqls', 'mariadbs', 'services']); - - return response()->json($environment); - } -} diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php new file mode 100644 index 000000000..4721b48e1 --- /dev/null +++ b/app/Http/Controllers/Api/ProjectController.php @@ -0,0 +1,60 @@ +select('id', 'name', 'uuid')->get(); + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($projects), + ]); + } + + public function project_by_uuid(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + $project = Project::whereTeamId($teamId)->whereUuid(request()->uuid)->first()->load(['environments']); + if (! $project) { + return response()->json(['success' => false, 'message' => 'Project not found.'], 404); + } + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($project), + ]); + } + + public function environment_details(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + $project = Project::whereTeamId($teamId)->whereUuid(request()->uuid)->first(); + $environment = $project->environments()->whereName(request()->environment_name)->first(); + if (! $environment) { + return response()->json(['success' => false, 'message' => 'Environment not found.'], 404); + } + $environment = $environment->load(['applications', 'postgresqls', 'redis', 'mongodbs', 'mysqls', 'mariadbs', 'services']); + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($environment), + ]); + } +} diff --git a/app/Http/Controllers/Api/Resources.php b/app/Http/Controllers/Api/ResourcesController.php similarity index 80% rename from app/Http/Controllers/Api/Resources.php rename to app/Http/Controllers/Api/ResourcesController.php index 0d538b62e..47dfc6733 100644 --- a/app/Http/Controllers/Api/Resources.php +++ b/app/Http/Controllers/Api/ResourcesController.php @@ -6,13 +6,13 @@ use App\Models\Project; use Illuminate\Http\Request; -class Resources extends Controller +class ResourcesController extends Controller { public function resources(Request $request) { - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } $projects = Project::where('team_id', $teamId)->get(); $resources = collect(); @@ -34,6 +34,9 @@ public function resources(Request $request) return $payload; }); - return response()->json($resources); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($resources), + ]); } } diff --git a/app/Http/Controllers/Api/SecurityController.php b/app/Http/Controllers/Api/SecurityController.php new file mode 100644 index 000000000..51c6fee26 --- /dev/null +++ b/app/Http/Controllers/Api/SecurityController.php @@ -0,0 +1,160 @@ +get(); + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($keys), + ]); + } + + public function key_by_uuid(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + $key = PrivateKey::where('team_id', $teamId)->where('uuid', $request->uuid)->first(); + + if (is_null($key)) { + return response()->json([ + 'success' => false, + 'message' => 'Key not found.', + ], 404); + } + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($key), + ]); + } + + public function create_key(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + $return = validateIncomingRequest($request); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } + $validator = customApiValidator($request->all(), [ + 'name' => 'string|max:255', + 'description' => 'string|max:255', + 'private_key' => 'required|string', + ]); + + if ($validator->fails()) { + $errors = $validator->errors(); + + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + if (! $request->name) { + $request->offsetSet('name', generate_random_name()); + } + if (! $request->description) { + $request->offsetSet('description', 'Created by Coolify via API'); + } + $key = PrivateKey::create([ + 'team_id' => $teamId, + 'name' => $request->name, + 'description' => $request->description, + 'private_key' => $request->private_key, + ]); + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($key), + ]); + } + + public function update_key(Request $request) + { + $allowedFields = ['name', 'description', 'private_key']; + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + $return = validateIncomingRequest($request); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } + + $validator = customApiValidator($request->all(), [ + 'name' => 'string|max:255', + 'description' => 'string|max:255', + 'private_key' => 'required|string', + ]); + + $extraFields = array_diff(array_keys($request->all()), $allowedFields); + if ($validator->fails() || ! empty($extraFields)) { + $errors = $validator->errors(); + if (! empty($extraFields)) { + foreach ($extraFields as $field) { + $errors->add($field, 'This field is not allowed.'); + } + } + + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + $foundKey = PrivateKey::where('team_id', $teamId)->where('uuid', $request->uuid)->first(); + if (is_null($foundKey)) { + return response()->json([ + 'success' => false, + 'message' => 'Key not found.', + ], 404); + } + $foundKey->update($request->all()); + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($foundKey), + ])->setStatusCode(201); + } + + public function delete_key(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + if (! $request->uuid) { + return response()->json(['success' => false, 'message' => 'UUID is required.'], 422); + } + + $key = PrivateKey::where('team_id', $teamId)->where('uuid', $request->uuid)->first(); + if (is_null($key)) { + return response()->json(['success' => false, 'message' => 'Key not found.'], 404); + } + $key->delete(); + + return response()->json([ + 'success' => true, + 'message' => 'Key deleted.', + ]); + } +} diff --git a/app/Http/Controllers/Api/Servers.php b/app/Http/Controllers/Api/ServersController.php similarity index 79% rename from app/Http/Controllers/Api/Servers.php rename to app/Http/Controllers/Api/ServersController.php index 387c4bd48..4d9479b7c 100644 --- a/app/Http/Controllers/Api/Servers.php +++ b/app/Http/Controllers/Api/ServersController.php @@ -8,14 +8,15 @@ use App\Models\Project; use App\Models\Server as ModelsServer; use Illuminate\Http\Request; +use Stringable; -class Servers extends Controller +class ServersController extends Controller { public function servers(Request $request) { - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } $servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port')->get()->load(['settings'])->map(function ($server) { $server['is_reachable'] = $server->settings->is_reachable; @@ -23,16 +24,22 @@ public function servers(Request $request) return $server; }); + $servers = $servers->map(function ($server) { + return serializeApiResponse($server); + }); - return response()->json($servers); + return response()->json([ + 'success' => true, + 'data' => $servers, + ]); } public function server_by_uuid(Request $request) { $with_resources = $request->query('resources'); - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } $server = ModelsServer::whereTeamId($teamId)->whereUuid(request()->uuid)->first(); if (is_null($server)) { @@ -60,22 +67,25 @@ public function server_by_uuid(Request $request) $server->load(['settings']); } - return response()->json($server); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($server), + ]); } public function get_domains_by_server(Request $request) { - $teamId = get_team_id_from_token(); + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { - return invalid_token(); + return invalidTokenResponse(); } - $uuid = $request->query->get('uuid'); + $uuid = $request->get('uuid'); if ($uuid) { $domains = Application::getDomainsByUuid($uuid); return response()->json([ - 'uuid' => $uuid, - 'domains' => $domains, + 'success' => true, + 'data' => serializeApiResponse($domains), ]); } $projects = Project::where('team_id', $teamId)->get(); @@ -86,8 +96,13 @@ public function get_domains_by_server(Request $request) foreach ($applications as $application) { $ip = $application->destination->server->ip; $fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) { - return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', ''); + $f = str($fqdn)->replace('http://', '')->replace('https://', '')->explode('/'); + + return str(str($f[0])->explode(':')[0]); + })->filter(function (Stringable $fqdn) { + return $fqdn->isNotEmpty(); }); + if ($ip === 'host.docker.internal') { if ($settings->public_ipv4) { $domains->push([ @@ -122,7 +137,11 @@ public function get_domains_by_server(Request $request) if ($service_applications->count() > 0) { foreach ($service_applications as $application) { $fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) { - return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', ''); + $f = str($fqdn)->replace('http://', '')->replace('https://', '')->explode('/'); + + return str(str($f[0])->explode(':')[0]); + })->filter(function (Stringable $fqdn) { + return $fqdn->isNotEmpty(); }); if ($ip === 'host.docker.internal') { if ($settings->public_ipv4) { @@ -162,6 +181,9 @@ public function get_domains_by_server(Request $request) ]; })->values(); - return response()->json($domains); + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($domains), + ]); } } diff --git a/app/Http/Controllers/Api/Team.php b/app/Http/Controllers/Api/Team.php deleted file mode 100644 index c895f2c1b..000000000 --- a/app/Http/Controllers/Api/Team.php +++ /dev/null @@ -1,74 +0,0 @@ -user()->teams; - - return response()->json($teams); - } - - public function team_by_id(Request $request) - { - $id = $request->id; - $teamId = get_team_id_from_token(); - if (is_null($teamId)) { - return invalid_token(); - } - $teams = auth()->user()->teams; - $team = $teams->where('id', $id)->first(); - if (is_null($team)) { - return response()->json(['error' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid'], 404); - } - - return response()->json($team); - } - - public function members_by_id(Request $request) - { - $id = $request->id; - $teamId = get_team_id_from_token(); - if (is_null($teamId)) { - return invalid_token(); - } - $teams = auth()->user()->teams; - $team = $teams->where('id', $id)->first(); - if (is_null($team)) { - return response()->json(['error' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid-members'], 404); - } - - return response()->json($team->members); - } - - public function current_team(Request $request) - { - $teamId = get_team_id_from_token(); - if (is_null($teamId)) { - return invalid_token(); - } - $team = auth()->user()->currentTeam(); - - return response()->json($team); - } - - public function current_team_members(Request $request) - { - $teamId = get_team_id_from_token(); - if (is_null($teamId)) { - return invalid_token(); - } - $team = auth()->user()->currentTeam(); - - return response()->json($team->members); - } -} diff --git a/app/Http/Controllers/Api/TeamController.php b/app/Http/Controllers/Api/TeamController.php new file mode 100644 index 000000000..a256e9caf --- /dev/null +++ b/app/Http/Controllers/Api/TeamController.php @@ -0,0 +1,89 @@ +user()->teams; + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($teams), + ]); + } + + public function team_by_id(Request $request) + { + $id = $request->id; + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + $teams = auth()->user()->teams; + $team = $teams->where('id', $id)->first(); + if (is_null($team)) { + return response()->json(['success' => false, 'message' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid'], 404); + } + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($team), + ]); + } + + public function members_by_id(Request $request) + { + $id = $request->id; + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + $teams = auth()->user()->teams; + $team = $teams->where('id', $id)->first(); + if (is_null($team)) { + return response()->json(['success' => false, 'message' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid-members'], 404); + } + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($team->members), + ]); + } + + public function current_team(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + $team = auth()->user()->currentTeam(); + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($team), + ]); + } + + public function current_team_members(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + $team = auth()->user()->currentTeam(); + + return response()->json([ + 'success' => true, + 'data' => serializeApiResponse($team->members), + ]); + } +} diff --git a/app/Http/Middleware/ApiAllowed.php b/app/Http/Middleware/ApiAllowed.php new file mode 100644 index 000000000..dc0a433e2 --- /dev/null +++ b/app/Http/Middleware/ApiAllowed.php @@ -0,0 +1,34 @@ +clearAll(); + if (isCloud()) { + return $next($request); + } + $settings = InstanceSettings::get(); + if ($settings->is_api_enabled === false) { + return response()->json(['success' => true, 'message' => 'API is disabled.'], 403); + } + + if (! isDev()) { + if ($settings->allowed_ips) { + $allowedIps = explode(',', $settings->allowed_ips); + if (! in_array($request->ip(), $allowedIps)) { + return response()->json(['success' => true, 'message' => 'You are not allowed to access the API.'], 403); + } + } + } + + return $next($request); + } +} diff --git a/app/Livewire/Settings/Configuration.php b/app/Livewire/Settings/Configuration.php index 3b6d7cd72..7439e112f 100644 --- a/app/Livewire/Settings/Configuration.php +++ b/app/Livewire/Settings/Configuration.php @@ -18,7 +18,8 @@ class Configuration extends Component public bool $is_dns_validation_enabled; - // public bool $next_channel; + public bool $is_api_enabled; + protected string $dynamic_config_path = '/data/coolify/proxy/dynamic'; protected Server $server; @@ -30,6 +31,7 @@ class Configuration extends Component 'settings.public_port_max' => 'required', 'settings.custom_dns_servers' => 'nullable', 'settings.instance_name' => 'nullable', + 'settings.allowed_ips' => 'nullable', ]; protected $validationAttributes = [ @@ -38,6 +40,7 @@ class Configuration extends Component 'settings.public_port_min' => 'Public port min', 'settings.public_port_max' => 'Public port max', 'settings.custom_dns_servers' => 'Custom DNS servers', + 'settings.allowed_ips' => 'Allowed IPs', ]; public function mount() @@ -45,8 +48,8 @@ public function mount() $this->do_not_track = $this->settings->do_not_track; $this->is_auto_update_enabled = $this->settings->is_auto_update_enabled; $this->is_registration_enabled = $this->settings->is_registration_enabled; - // $this->next_channel = $this->settings->next_channel; $this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled; + $this->is_api_enabled = $this->settings->is_api_enabled; } public function instantSave() @@ -55,12 +58,7 @@ public function instantSave() $this->settings->is_auto_update_enabled = $this->is_auto_update_enabled; $this->settings->is_registration_enabled = $this->is_registration_enabled; $this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled; - // if ($this->next_channel) { - // $this->settings->next_channel = false; - // $this->next_channel = false; - // } else { - // $this->settings->next_channel = $this->next_channel; - // } + $this->settings->is_api_enabled = $this->is_api_enabled; $this->settings->save(); $this->dispatch('success', 'Settings updated!'); } @@ -94,6 +92,13 @@ public function submit() $this->settings->custom_dns_servers = $this->settings->custom_dns_servers->unique(); $this->settings->custom_dns_servers = $this->settings->custom_dns_servers->implode(','); + $this->settings->allowed_ips = str($this->settings->allowed_ips)->replaceEnd(',', '')->trim(); + $this->settings->allowed_ips = str($this->settings->allowed_ips)->trim()->explode(',')->map(function ($ip) { + return str($ip)->trim(); + }); + $this->settings->allowed_ips = $this->settings->allowed_ips->unique(); + $this->settings->allowed_ips = $this->settings->allowed_ips->implode(','); + $this->settings->save(); $this->server->setupDynamicProxyConfiguration(); if (! $error_show) { diff --git a/app/Models/InstanceSettings.php b/app/Models/InstanceSettings.php index 38f79ce75..bd3c41a1f 100644 --- a/app/Models/InstanceSettings.php +++ b/app/Models/InstanceSettings.php @@ -17,6 +17,7 @@ class InstanceSettings extends Model implements SendsEmail protected $casts = [ 'resale_license' => 'encrypted', 'smtp_password' => 'encrypted', + 'allowed_ip_ranges' => 'array', ]; public function fqdn(): Attribute diff --git a/app/Models/StandaloneClickhouse.php b/app/Models/StandaloneClickhouse.php index e968db18d..673224650 100644 --- a/app/Models/StandaloneClickhouse.php +++ b/app/Models/StandaloneClickhouse.php @@ -13,6 +13,8 @@ class StandaloneClickhouse extends BaseModel protected $guarded = []; + protected $appends = ['internal_db_url', 'external_db_url', 'database_type']; + protected $casts = [ 'clickhouse_password' => 'encrypted', ]; @@ -178,17 +180,44 @@ public function team() return data_get($this, 'environment.project.team'); } + public function databaseType(): Attribute + { + return new Attribute( + get: fn () => $this->type(), + ); + } + public function type(): string { return 'standalone-clickhouse'; } - public function get_db_url(bool $useInternal = false): string + protected function internalDbUrl(): Attribute + { + return new Attribute( + get: fn () => "clickhouse://{$this->clickhouse_user}:{$this->clickhouse_password}@{$this->uuid}:9000/{$this->clickhouse_db}", + ); + } + + protected function externalDbUrl(): Attribute + { + return new Attribute( + get: function () { + if ($this->is_public && $this->public_port) { + return "clickhouse://{$this->clickhouse_user}:{$this->clickhouse_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->clickhouse_db}"; + } + + return null; + } + ); + } + + public function get_db_url(bool $useInternal = false) { if ($this->is_public && ! $useInternal) { - return "clickhouse://{$this->clickhouse_user}:{$this->clickhouse_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->clickhouse_db}"; + return $this->externalDbUrl; } else { - return "clickhouse://{$this->clickhouse_user}:{$this->clickhouse_password}@{$this->uuid}:9000/{$this->clickhouse_db}"; + return $this->internalDbUrl; } } diff --git a/app/Models/StandaloneDragonfly.php b/app/Models/StandaloneDragonfly.php index c6718acfe..d78d656c1 100644 --- a/app/Models/StandaloneDragonfly.php +++ b/app/Models/StandaloneDragonfly.php @@ -13,6 +13,8 @@ class StandaloneDragonfly extends BaseModel protected $guarded = []; + protected $appends = ['internal_db_url', 'external_db_url', 'database_type']; + protected $casts = [ 'dragonfly_password' => 'encrypted', ]; @@ -178,17 +180,44 @@ public function portsMappingsArray(): Attribute ); } + public function databaseType(): Attribute + { + return new Attribute( + get: fn () => $this->type(), + ); + } + public function type(): string { return 'standalone-dragonfly'; } - public function get_db_url(bool $useInternal = false): string + protected function internalDbUrl(): Attribute + { + return new Attribute( + get: fn () => "redis://:{$this->dragonfly_password}@{$this->uuid}:6379/0", + ); + } + + protected function externalDbUrl(): Attribute + { + return new Attribute( + get: function () { + if ($this->is_public && $this->public_port) { + return "redis://:{$this->dragonfly_password}@{$this->destination->server->getIp}:{$this->public_port}/0"; + } + + return null; + } + ); + } + + public function get_db_url(bool $useInternal = false) { if ($this->is_public && ! $useInternal) { - return "redis://:{$this->dragonfly_password}@{$this->destination->server->getIp}:{$this->public_port}/0"; + return $this->externalDbUrl; } else { - return "redis://:{$this->dragonfly_password}@{$this->uuid}:6379/0"; + return $this->internalDbUrl; } } diff --git a/app/Models/StandaloneKeydb.php b/app/Models/StandaloneKeydb.php index 142f960aa..7b71bd55f 100644 --- a/app/Models/StandaloneKeydb.php +++ b/app/Models/StandaloneKeydb.php @@ -13,6 +13,8 @@ class StandaloneKeydb extends BaseModel protected $guarded = []; + protected $appends = ['internal_db_url', 'external_db_url']; + protected $casts = [ 'keydb_password' => 'encrypted', ]; @@ -178,17 +180,44 @@ public function portsMappingsArray(): Attribute ); } + public function databaseType(): Attribute + { + return new Attribute( + get: fn () => $this->type(), + ); + } + public function type(): string { return 'standalone-keydb'; } - public function get_db_url(bool $useInternal = false): string + protected function internalDbUrl(): Attribute + { + return new Attribute( + get: fn () => "redis://{$this->keydb_password}@{$this->uuid}:6379/0", + ); + } + + protected function externalDbUrl(): Attribute + { + return new Attribute( + get: function () { + if ($this->is_public && $this->public_port) { + return "redis://{$this->keydb_password}@{$this->destination->server->getIp}:{$this->public_port}/0"; + } + + return null; + } + ); + } + + public function get_db_url(bool $useInternal = false) { if ($this->is_public && ! $useInternal) { - return "redis://{$this->keydb_password}@{$this->destination->server->getIp}:{$this->public_port}/0"; + return $this->externalDbUrl; } else { - return "redis://{$this->keydb_password}@{$this->uuid}:6379/0"; + return $this->internalDbUrl; } } diff --git a/app/Models/StandaloneMariadb.php b/app/Models/StandaloneMariadb.php index 7e6d2e0d1..00df4fe71 100644 --- a/app/Models/StandaloneMariadb.php +++ b/app/Models/StandaloneMariadb.php @@ -13,6 +13,8 @@ class StandaloneMariadb extends BaseModel protected $guarded = []; + protected $appends = ['internal_db_url', 'external_db_url', 'database_type']; + protected $casts = [ 'mariadb_password' => 'encrypted', ]; @@ -161,6 +163,13 @@ public function isLogDrainEnabled() return data_get($this, 'is_log_drain_enabled', false); } + public function databaseType(): Attribute + { + return new Attribute( + get: fn () => $this->type(), + ); + } + public function type(): string { return 'standalone-mariadb'; @@ -183,12 +192,32 @@ public function portsMappingsArray(): Attribute ); } - public function get_db_url(bool $useInternal = false): string + protected function internalDbUrl(): Attribute + { + return new Attribute( + get: fn () => "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->uuid}:3306/{$this->mariadb_database}", + ); + } + + protected function externalDbUrl(): Attribute + { + return new Attribute( + get: function () { + if ($this->is_public && $this->public_port) { + return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mariadb_database}"; + } + + return null; + } + ); + } + + public function get_db_url(bool $useInternal = false) { if ($this->is_public && ! $useInternal) { - return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mariadb_database}"; + return $this->externalDbUrl; } else { - return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->uuid}:3306/{$this->mariadb_database}"; + return $this->internalDbUrl; } } diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php index df895bb34..0863522a8 100644 --- a/app/Models/StandaloneMongodb.php +++ b/app/Models/StandaloneMongodb.php @@ -13,6 +13,8 @@ class StandaloneMongodb extends BaseModel protected $guarded = []; + protected $appends = ['internal_db_url', 'external_db_url', 'database_type']; + protected static function booted() { static::created(function ($database) { @@ -198,17 +200,44 @@ public function portsMappingsArray(): Attribute ); } + public function databaseType(): Attribute + { + return new Attribute( + get: fn () => $this->type(), + ); + } + public function type(): string { return 'standalone-mongodb'; } + protected function internalDbUrl(): Attribute + { + return new Attribute( + get: fn () => "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->uuid}:27017/?directConnection=true", + ); + } + + protected function externalDbUrl(): Attribute + { + return new Attribute( + get: function () { + if ($this->is_public && $this->public_port) { + return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true"; + } + + return null; + } + ); + } + public function get_db_url(bool $useInternal = false) { if ($this->is_public && ! $useInternal) { - return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true"; + return $this->externalDbUrl; } else { - return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->uuid}:27017/?directConnection=true"; + return $this->internalDbUrl; } } diff --git a/app/Models/StandaloneMysql.php b/app/Models/StandaloneMysql.php index bd160f877..79e7c37fa 100644 --- a/app/Models/StandaloneMysql.php +++ b/app/Models/StandaloneMysql.php @@ -13,6 +13,8 @@ class StandaloneMysql extends BaseModel protected $guarded = []; + protected $appends = ['internal_db_url', 'external_db_url', 'database_type']; + protected $casts = [ 'mysql_password' => 'encrypted', 'mysql_root_password' => 'encrypted', @@ -157,6 +159,13 @@ public function link() return null; } + public function databaseType(): Attribute + { + return new Attribute( + get: fn () => $this->type(), + ); + } + public function type(): string { return 'standalone-mysql'; @@ -184,12 +193,32 @@ public function portsMappingsArray(): Attribute ); } - public function get_db_url(bool $useInternal = false): string + protected function internalDbUrl(): Attribute + { + return new Attribute( + get: fn () => "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->uuid}:3306/{$this->mysql_database}", + ); + } + + protected function externalDbUrl(): Attribute + { + return new Attribute( + get: function () { + if ($this->is_public && $this->public_port) { + return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mysql_database}"; + } + + return null; + } + ); + } + + public function get_db_url(bool $useInternal = false) { if ($this->is_public && ! $useInternal) { - return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mysql_database}"; + return $this->externalDbUrl; } else { - return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->uuid}:3306/{$this->mysql_database}"; + return $this->internalDbUrl; } } diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php index 114d376e8..1d5276cf3 100644 --- a/app/Models/StandalonePostgresql.php +++ b/app/Models/StandalonePostgresql.php @@ -13,6 +13,8 @@ class StandalonePostgresql extends BaseModel protected $guarded = []; + protected $appends = ['internal_db_url', 'external_db_url', 'database_type']; + protected $casts = [ 'init_scripts' => 'array', 'postgres_password' => 'encrypted', @@ -179,17 +181,44 @@ public function team() return data_get($this, 'environment.project.team'); } + public function databaseType(): Attribute + { + return new Attribute( + get: fn () => $this->type(), + ); + } + public function type(): string { return 'standalone-postgresql'; } - public function get_db_url(bool $useInternal = false): string + protected function internalDbUrl(): Attribute + { + return new Attribute( + get: fn () => "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->uuid}:5432/{$this->postgres_db}", + ); + } + + protected function externalDbUrl(): Attribute + { + return new Attribute( + get: function () { + if ($this->is_public && $this->public_port) { + return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->postgres_db}"; + } + + return null; + } + ); + } + + public function get_db_url(bool $useInternal = false) { if ($this->is_public && ! $useInternal) { - return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->postgres_db}"; + return $this->externalDbUrl; } else { - return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->uuid}:5432/{$this->postgres_db}"; + return $this->internalDbUrl; } } diff --git a/app/Models/StandaloneRedis.php b/app/Models/StandaloneRedis.php index 022cd8d09..e0f863aca 100644 --- a/app/Models/StandaloneRedis.php +++ b/app/Models/StandaloneRedis.php @@ -13,6 +13,8 @@ class StandaloneRedis extends BaseModel protected $guarded = []; + protected $appends = ['internal_db_url', 'external_db_url', 'database_type']; + protected static function booted() { static::created(function ($database) { @@ -179,12 +181,39 @@ public function type(): string return 'standalone-redis'; } - public function get_db_url(bool $useInternal = false): string + public function databaseType(): Attribute + { + return new Attribute( + get: fn () => $this->type(), + ); + } + + protected function internalDbUrl(): Attribute + { + return new Attribute( + get: fn () => "redis://:{$this->redis_password}@{$this->uuid}:6379/0", + ); + } + + protected function externalDbUrl(): Attribute + { + return new Attribute( + get: function () { + if ($this->is_public && $this->public_port) { + return "redis://:{$this->redis_password}@{$this->destination->server->getIp}:{$this->public_port}/0"; + } + + return null; + } + ); + } + + public function get_db_url(bool $useInternal = false) { if ($this->is_public && ! $useInternal) { - return "redis://:{$this->redis_password}@{$this->destination->server->getIp}:{$this->public_port}/0"; + return $this->externalDbUrl; } else { - return "redis://:{$this->redis_password}@{$this->uuid}:6379/0"; + return $this->internalDbUrl; } } diff --git a/bootstrap/helpers/api.php b/bootstrap/helpers/api.php index f5c99dbda..c5083534f 100644 --- a/bootstrap/helpers/api.php +++ b/bootstrap/helpers/api.php @@ -3,25 +3,27 @@ use App\Enums\BuildPackTypes; use App\Enums\RedirectTypes; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Http\Request; use Illuminate\Validation\Rule; -function get_team_id_from_token() +function getTeamIdFromToken() { $token = auth()->user()->currentAccessToken(); return data_get($token, 'team_id'); } -function invalid_token() +function invalidTokenResponse() { - return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api-reference/authorization'], 400); + return response()->json(['success' => false, 'message' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api-reference/authorization'], 400); } -function serialize_api_response($data) +function serializeApiResponse($data) { if (! $data instanceof Collection) { $data = collect($data); } $data = $data->sortKeys(); + $created_at = data_get($data, 'created_at'); $updated_at = data_get($data, 'updated_at'); if ($created_at) { @@ -33,6 +35,16 @@ function serialize_api_response($data) unset($data['updated_at']); $data['updated_at'] = $updated_at; } + if (data_get($data, 'name')) { + $data = $data->prepend($data['name'], 'name'); + } + if (data_get($data, 'description')) { + $data = $data->prepend($data['description'], 'description'); + } + if (data_get($data, 'uuid')) { + $data = $data->prepend($data['uuid'], 'uuid'); + } + if (data_get($data, 'id')) { $data = $data->prepend($data['id'], 'id'); } @@ -90,3 +102,36 @@ function sharedDataApplications() 'manual_webhook_secret_gitea' => 'string|nullable', ]; } + +function validateIncomingRequest(Request $request) +{ + // check if request is json + if (! $request->isJson()) { + return response()->json([ + 'success' => false, + 'message' => 'Invalid request.', + 'error' => 'Content-Type must be application/json.', + ], 400); + } + // check if request is valid json + if (! json_decode($request->getContent())) { + return response()->json([ + 'success' => false, + 'message' => 'Invalid request.', + 'error' => 'Invalid JSON.', + ], 400); + } +} + +function removeUnnecessaryFieldsFromRequest(Request $request) +{ + $request->offsetUnset('project_uuid'); + $request->offsetUnset('environment_name'); + $request->offsetUnset('destination_uuid'); + $request->offsetUnset('server_uuid'); + $request->offsetUnset('type'); + $request->offsetUnset('domains'); + $request->offsetUnset('instant_deploy'); + $request->offsetUnset('github_app_uuid'); + $request->offsetUnset('private_key_uuid'); +} diff --git a/bootstrap/helpers/databases.php b/bootstrap/helpers/databases.php index dba8aa543..ef3f8ac9b 100644 --- a/bootstrap/helpers/databases.php +++ b/bootstrap/helpers/databases.php @@ -19,131 +19,163 @@ function generate_database_name(string $type): string return $type.'-database-'.$cuid; } -function create_standalone_postgresql($environment_id, $destination_uuid): StandalonePostgresql +function create_standalone_postgresql($environmentId, $destinationUuid, ?array $otherData = null): StandalonePostgresql { - // TODO: If another type of destination is added, this will need to be updated. - $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); + $destination = StandaloneDocker::where('uuid', $destinationUuid)->first(); if (! $destination) { throw new Exception('Destination not found'); } + $database = new StandalonePostgresql(); + $database->name = generate_database_name('postgresql'); + $database->postgres_password = \Illuminate\Support\Str::password(length: 64, symbols: false); + $database->environment_id = $environmentId; + $database->destination_id = $destination->id; + $database->destination_type = $destination->getMorphClass(); + if ($otherData) { + $database->fill($otherData); + } + $database->save(); - return StandalonePostgresql::create([ - 'name' => generate_database_name('postgresql'), - 'postgres_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), - 'environment_id' => $environment_id, - 'destination_id' => $destination->id, - 'destination_type' => $destination->getMorphClass(), - ]); + return $database; } -function create_standalone_redis($environment_id, $destination_uuid): StandaloneRedis +function create_standalone_redis($environment_id, $destination_uuid, ?array $otherData = null): StandaloneRedis { $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); if (! $destination) { throw new Exception('Destination not found'); } + $database = new StandaloneRedis(); + $database->name = generate_database_name('redis'); + $database->redis_password = \Illuminate\Support\Str::password(length: 64, symbols: false); + $database->environment_id = $environment_id; + $database->destination_id = $destination->id; + $database->destination_type = $destination->getMorphClass(); + if ($otherData) { + $database->fill($otherData); + } + $database->save(); - return StandaloneRedis::create([ - 'name' => generate_database_name('redis'), - 'redis_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), - 'environment_id' => $environment_id, - 'destination_id' => $destination->id, - 'destination_type' => $destination->getMorphClass(), - ]); + return $database; } -function create_standalone_mongodb($environment_id, $destination_uuid): StandaloneMongodb +function create_standalone_mongodb($environment_id, $destination_uuid, ?array $otherData = null): StandaloneMongodb { $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); if (! $destination) { throw new Exception('Destination not found'); } + $database = new StandaloneMongodb(); + $database->name = generate_database_name('mongodb'); + $database->mongo_initdb_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false); + $database->environment_id = $environment_id; + $database->destination_id = $destination->id; + $database->destination_type = $destination->getMorphClass(); + if ($otherData) { + $database->fill($otherData); + } + $database->save(); - return StandaloneMongodb::create([ - 'name' => generate_database_name('mongodb'), - 'mongo_initdb_root_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), - 'environment_id' => $environment_id, - 'destination_id' => $destination->id, - 'destination_type' => $destination->getMorphClass(), - ]); + return $database; } -function create_standalone_mysql($environment_id, $destination_uuid): StandaloneMysql +function create_standalone_mysql($environment_id, $destination_uuid, ?array $otherData = null): StandaloneMysql { $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); if (! $destination) { throw new Exception('Destination not found'); } + $database = new StandaloneMysql(); + $database->name = generate_database_name('mysql'); + $database->mysql_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false); + $database->mysql_password = \Illuminate\Support\Str::password(length: 64, symbols: false); + $database->environment_id = $environment_id; + $database->destination_id = $destination->id; + $database->destination_type = $destination->getMorphClass(); + if ($otherData) { + $database->fill($otherData); + } + $database->save(); - return StandaloneMysql::create([ - 'name' => generate_database_name('mysql'), - 'mysql_root_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), - 'mysql_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), - 'environment_id' => $environment_id, - 'destination_id' => $destination->id, - 'destination_type' => $destination->getMorphClass(), - ]); + return $database; } -function create_standalone_mariadb($environment_id, $destination_uuid): StandaloneMariadb +function create_standalone_mariadb($environment_id, $destination_uuid, ?array $otherData = null): StandaloneMariadb { $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); if (! $destination) { throw new Exception('Destination not found'); } + $database = new StandaloneMariadb(); + $database->name = generate_database_name('mariadb'); + $database->mariadb_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false); + $database->mariadb_password = \Illuminate\Support\Str::password(length: 64, symbols: false); + $database->environment_id = $environment_id; + $database->destination_id = $destination->id; + $database->destination_type = $destination->getMorphClass(); - return StandaloneMariadb::create([ - 'name' => generate_database_name('mariadb'), - 'mariadb_root_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), - 'mariadb_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), - 'environment_id' => $environment_id, - 'destination_id' => $destination->id, - 'destination_type' => $destination->getMorphClass(), - ]); + if ($otherData) { + $database->fill($otherData); + } + $database->save(); + + return $database; } -function create_standalone_keydb($environment_id, $destination_uuid): StandaloneKeydb +function create_standalone_keydb($environment_id, $destination_uuid, ?array $otherData = null): StandaloneKeydb { $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); if (! $destination) { throw new Exception('Destination not found'); } + $database = new StandaloneKeydb(); + $database->name = generate_database_name('keydb'); + $database->keydb_password = \Illuminate\Support\Str::password(length: 64, symbols: false); + $database->environment_id = $environment_id; + $database->destination_id = $destination->id; + $database->destination_type = $destination->getMorphClass(); + if ($otherData) { + $database->fill($otherData); + } + $database->save(); - return StandaloneKeydb::create([ - 'name' => generate_database_name('keydb'), - 'keydb_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), - 'environment_id' => $environment_id, - 'destination_id' => $destination->id, - 'destination_type' => $destination->getMorphClass(), - ]); + return $database; } -function create_standalone_dragonfly($environment_id, $destination_uuid): StandaloneDragonfly +function create_standalone_dragonfly($environment_id, $destination_uuid, ?array $otherData = null): StandaloneDragonfly { $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); if (! $destination) { throw new Exception('Destination not found'); } + $database = new StandaloneDragonfly(); + $database->name = generate_database_name('dragonfly'); + $database->dragonfly_password = \Illuminate\Support\Str::password(length: 64, symbols: false); + $database->environment_id = $environment_id; + $database->destination_id = $destination->id; + $database->destination_type = $destination->getMorphClass(); + if ($otherData) { + $database->fill($otherData); + } + $database->save(); - return StandaloneDragonfly::create([ - 'name' => generate_database_name('dragonfly'), - 'dragonfly_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), - 'environment_id' => $environment_id, - 'destination_id' => $destination->id, - 'destination_type' => $destination->getMorphClass(), - ]); + return $database; } -function create_standalone_clickhouse($environment_id, $destination_uuid): StandaloneClickhouse +function create_standalone_clickhouse($environment_id, $destination_uuid, ?array $otherData = null): StandaloneClickhouse { $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); if (! $destination) { throw new Exception('Destination not found'); } + $database = new StandaloneClickhouse(); + $database->name = generate_database_name('clickhouse'); + $database->clickhouse_admin_password = \Illuminate\Support\Str::password(length: 64, symbols: false); + $database->environment_id = $environment_id; + $database->destination_id = $destination->id; + $database->destination_type = $destination->getMorphClass(); + if ($otherData) { + $database->fill($otherData); + } + $database->save(); - return StandaloneClickhouse::create([ - 'name' => generate_database_name('clickhouse'), - 'clickhouse_admin_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), - 'environment_id' => $environment_id, - 'destination_id' => $destination->id, - 'destination_type' => $destination->getMorphClass(), - ]); + return $database; } /** diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 1d70b674c..5efc0f9ef 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -538,6 +538,43 @@ function getResourceByUuid(string $uuid, ?int $teamId = null) return null; } +function queryDatabaseByUuidWithinTeam(string $uuid, string $teamId) +{ + $postgresql = StandalonePostgresql::whereUuid($uuid)->first(); + if ($postgresql && $postgresql->team()->id == $teamId) { + return $postgresql->unsetRelation('environment')->unsetRelation('destination'); + } + $redis = StandaloneRedis::whereUuid($uuid)->first(); + if ($redis && $redis->team()->id == $teamId) { + return $redis->unsetRelation('environment'); + } + $mongodb = StandaloneMongodb::whereUuid($uuid)->first(); + if ($mongodb && $mongodb->team()->id == $teamId) { + return $mongodb->unsetRelation('environment'); + } + $mysql = StandaloneMysql::whereUuid($uuid)->first(); + if ($mysql && $mysql->team()->id == $teamId) { + return $mysql->unsetRelation('environment'); + } + $mariadb = StandaloneMariadb::whereUuid($uuid)->first(); + if ($mariadb && $mariadb->team()->id == $teamId) { + return $mariadb->unsetRelation('environment'); + } + $keydb = StandaloneKeydb::whereUuid($uuid)->first(); + if ($keydb && $keydb->team()->id == $teamId) { + return $keydb->unsetRelation('environment'); + } + $dragonfly = StandaloneDragonfly::whereUuid($uuid)->first(); + if ($dragonfly && $dragonfly->team()->id == $teamId) { + return $dragonfly->unsetRelation('environment'); + } + $clickhouse = StandaloneClickhouse::whereUuid($uuid)->first(); + if ($clickhouse && $clickhouse->team()->id == $teamId) { + return $clickhouse->unsetRelation('environment'); + } + + return null; +} function queryResourcesByUuid(string $uuid) { $resource = null; diff --git a/database/migrations/2024_07_01_115528_add_is_api_allowed_and_iplist.php b/database/migrations/2024_07_01_115528_add_is_api_allowed_and_iplist.php new file mode 100644 index 000000000..b319adb70 --- /dev/null +++ b/database/migrations/2024_07_01_115528_add_is_api_allowed_and_iplist.php @@ -0,0 +1,24 @@ +boolean('is_api_enabled')->default(true); + $table->text('allowed_ips')->nullable(); + }); + } + + public function down(): void + { + Schema::table('instance_settings', function (Blueprint $table) { + $table->dropColumn('is_api_enabled'); + $table->dropColumn('allowed_ips'); + }); + } +}; diff --git a/resources/views/livewire/settings/configuration.blade.php b/resources/views/livewire/settings/configuration.blade.php index b1c399bc3..b5fb49d3e 100644 --- a/resources/views/livewire/settings/configuration.blade.php +++ b/resources/views/livewire/settings/configuration.blade.php @@ -25,7 +25,16 @@ --}} +

API

+ +
+ +
+ +

Advanced

@if (!is_null(env('AUTOUPDATE', null))) @@ -36,13 +45,5 @@ @endif - {{-- @if ($next_channel) - - @else - - @endif --}}
diff --git a/routes/api.php b/routes/api.php index f4d9d786e..69eead3ba 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,11 +1,16 @@ json(['message' => 'Feedback sent.'], 200); + return response()->json(['success' => true, 'message' => 'Feedback sent.'], 200); }); Route::group([ 'middleware' => ['auth:sanctum'], 'prefix' => 'v1', +], function () { + Route::get('/enable', function () { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + if ($teamId !== '0') { + return response()->json(['success' => false, 'message' => 'You are not allowed to enable the API.'], 403); + } + $settings = InstanceSettings::get(); + $settings->update(['is_api_enabled' => true]); + + return response()->json(['success' => true, 'message' => 'API enabled.'], 200); + }); + Route::get('/disable', function () { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + if ($teamId !== '0') { + return response()->json(['success' => false, 'message' => 'You are not allowed to disable the API.'], 403); + } + $settings = InstanceSettings::get(); + $settings->update(['is_api_enabled' => false]); + + return response()->json(['success' => true, 'message' => 'API disabled.'], 200); + }); + +}); +Route::group([ + 'middleware' => ['auth:sanctum', ApiAllowed::class], + 'prefix' => 'v1', ], function () { Route::get('/version', function () { return response(config('version')); }); - Route::match(['get', 'post'], '/deploy', [Deploy::class, 'deploy']); - Route::get('/deployments', [Deploy::class, 'deployments']); - Route::get('/deployments/{uuid}', [Deploy::class, 'deployment_by_uuid']); - // Add environments endpoints - Route::get('/servers', [Servers::class, 'servers']); - Route::get('/servers/{uuid}', [Servers::class, 'server_by_uuid']); - Route::get('/servers/domains', [Servers::class, 'get_domains_by_server']); + Route::get('/teams', [TeamController::class, 'teams']); + Route::get('/teams/current', [TeamController::class, 'current_team']); + Route::get('/teams/current/members', [TeamController::class, 'current_team_members']); + Route::get('/teams/{id}', [TeamController::class, 'team_by_id']); + Route::get('/teams/{id}/members', [TeamController::class, 'members_by_id']); - Route::get('/resources', [Resources::class, 'resources']); + Route::get('/projects', [ProjectController::class, 'projects']); + Route::get('/projects/{uuid}', [ProjectController::class, 'project_by_uuid']); + Route::get('/projects/{uuid}/{environment_name}', [ProjectController::class, 'environment_details']); - Route::get('/applications', [Applications::class, 'applications']); - Route::post('/applications', [Applications::class, 'create_application']); + Route::get('/security/keys', [SecurityController::class, 'keys']); + Route::post('/security/keys', [SecurityController::class, 'create_key']); - Route::get('/applications/{uuid}', [Applications::class, 'application_by_uuid']); - Route::patch('/applications/{uuid}', [Applications::class, 'update_by_uuid']); - Route::delete('/applications/{uuid}', [Applications::class, 'delete_by_uuid']); + Route::get('/security/keys/{uuid}', [SecurityController::class, 'key_by_uuid']); + Route::patch('/security/keys/{uuid}', [SecurityController::class, 'update_key']); + Route::delete('/security/keys/{uuid}', [SecurityController::class, 'delete_key']); - Route::get('/applications/{uuid}/envs', [Applications::class, 'envs_by_uuid']); - Route::post('/applications/{uuid}/envs', [Applications::class, 'create_env']); - Route::post('/applications/{uuid}/envs/bulk', [Applications::class, 'create_bulk_envs']); - Route::patch('/applications/{uuid}/envs', [Applications::class, 'update_env_by_uuid']); - Route::delete('/applications/{uuid}/envs/{env_uuid}', [Applications::class, 'delete_env_by_uuid']); + Route::match(['get', 'post'], '/deploy', [DeployController::class, 'deploy']); - Route::match(['get', 'post'], '/applications/{uuid}/action/deploy', [Applications::class, 'action_deploy']); - Route::match(['get', 'post'], '/applications/{uuid}/action/restart', [Applications::class, 'action_restart']); - Route::match(['get', 'post'], '/applications/{uuid}/action/stop', [Applications::class, 'action_stop']); + Route::get('/deployments', [DeployController::class, 'deployments']); + Route::get('/deployments/{uuid}', [DeployController::class, 'deployment_by_uuid']); - Route::delete('/envs/{env_uuid}', [EnvironmentVariables::class, 'delete_env_by_uuid']); + Route::get('/servers', [ServersController::class, 'servers']); + Route::get('/servers/{uuid}', [ServersController::class, 'server_by_uuid']); + Route::get('/servers/{uuid}/domains', [ServersController::class, 'get_domains_by_server']); - Route::get('/teams', [Team::class, 'teams']); - Route::get('/teams/current', [Team::class, 'current_team']); - Route::get('/teams/current/members', [Team::class, 'current_team_members']); - Route::get('/teams/{id}', [Team::class, 'team_by_id']); - Route::get('/teams/{id}/members', [Team::class, 'members_by_id']); + Route::get('/resources', [ResourcesController::class, 'resources']); + + Route::get('/applications', [ApplicationsController::class, 'applications']); + Route::post('/applications', [ApplicationsController::class, 'create_application']); + + Route::get('/applications/{uuid}', [ApplicationsController::class, 'application_by_uuid']); + Route::patch('/applications/{uuid}', [ApplicationsController::class, 'update_by_uuid']); + Route::delete('/applications/{uuid}', [ApplicationsController::class, 'delete_by_uuid']); + + Route::get('/applications/{uuid}/envs', [ApplicationsController::class, 'envs_by_uuid']); + Route::post('/applications/{uuid}/envs', [ApplicationsController::class, 'create_env']); + Route::post('/applications/{uuid}/envs/bulk', [ApplicationsController::class, 'create_bulk_envs']); + Route::patch('/applications/{uuid}/envs', [ApplicationsController::class, 'update_env_by_uuid']); + Route::delete('/applications/{uuid}/envs/{env_uuid}', [ApplicationsController::class, 'delete_env_by_uuid']); + + Route::match(['get', 'post'], '/applications/{uuid}/action/deploy', [ApplicationsController::class, 'action_deploy']); + Route::match(['get', 'post'], '/applications/{uuid}/action/restart', [ApplicationsController::class, 'action_restart']); + Route::match(['get', 'post'], '/applications/{uuid}/action/stop', [ApplicationsController::class, 'action_stop']); + + Route::get('/databases', [DatabasesController::class, 'databases']); + Route::post('/databases', [DatabasesController::class, 'create_database']); + Route::get('/databases/{uuid}', [DatabasesController::class, 'database_by_uuid']); + // Route::patch('/databases/{uuid}', [DatabasesController::class, 'update_by_uuid']); + Route::delete('/databases/{uuid}', [DatabasesController::class, 'delete_by_uuid']); + + Route::delete('/envs/{env_uuid}', [EnvironmentVariablesController::class, 'delete_env_by_uuid']); - // Route::get('/projects', [Project::class, 'projects']); - //Route::get('/project/{uuid}', [Project::class, 'project_by_uuid']); - //Route::get('/project/{uuid}/{environment_name}', [Project::class, 'environment_details']); }); Route::any('/{any}', function () { - return response()->json(['error' => 'Not found.'], 404); + return response()->json(['success' => false, 'message' => 'Not found.', 'docs' => 'https://coolify.io/docs'], 404); })->where('any', '.*'); // Route::middleware(['throttle:5'])->group(function () { From 1249b1ece9cbac10c1e76491568c93fda7db681a Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 2 Jul 2024 10:02:43 +0200 Subject: [PATCH 25/54] fix: custom container name will be the container name, not just internal network name --- app/Jobs/ApplicationDeploymentJob.php | 58 +++++++------------ .../project/application/advanced.blade.php | 14 +++-- 2 files changed, 28 insertions(+), 44 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index b53c56d1c..229cb2532 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -194,6 +194,9 @@ public function __construct(int $application_deployment_queue_id) $this->is_debug_enabled = $this->application->settings->is_debug_enabled; $this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id); + if ($this->application->settings->custom_internal_name && ! $this->application->settings->is_consistent_container_name_enabled) { + $this->container_name = $this->application->settings->custom_internal_name; + } ray('New container name: ', $this->container_name); savePrivateKeyToFs($this->server); @@ -1570,23 +1573,6 @@ private function generate_compose_file() ], ], ]; - if (isset($this->application->settings->custom_internal_name)) { - $docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['aliases'][] = $this->application->settings->custom_internal_name; - } - // if (str($this->saved_outputs->get('dotenv'))->isNotEmpty()) { - // if (data_get($docker_compose, "services.{$this->container_name}.env_file")) { - // $docker_compose['services'][$this->container_name]['env_file'][] = '.env'; - // } else { - // $docker_compose['services'][$this->container_name]['env_file'] = ['.env']; - // } - // } - // if ($this->env_filename) { - // if (data_get($docker_compose, "services.{$this->container_name}.env_file")) { - // $docker_compose['services'][$this->container_name]['env_file'][] = $this->env_filename; - // } else { - // $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename]; - // } - // } if (! is_null($this->env_filename)) { $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename]; } @@ -1697,32 +1683,28 @@ private function generate_compose_file() if (count($volume_names) > 0) { $docker_compose['volumes'] = $volume_names; } - // if ($this->build_pack === 'dockerfile') { - // $docker_compose['services'][$this->container_name]['build'] = [ - // 'context' => $this->workdir, - // 'dockerfile' => $this->workdir . $this->dockerfile_location, - // ]; - // } if ($this->pull_request_id === 0) { $custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options); if ((bool) $this->application->settings->is_consistent_container_name_enabled) { - $docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name]; - if (count($custom_compose) > 0) { - $ipv4 = data_get($custom_compose, 'ip.0'); - $ipv6 = data_get($custom_compose, 'ip6.0'); - data_forget($custom_compose, 'ip'); - data_forget($custom_compose, 'ip6'); - if ($ipv4 || $ipv6) { - data_forget($docker_compose['services'][$this->application->uuid], 'networks'); + if (! $this->application->settings->custom_internal_name) { + $docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name]; + if (count($custom_compose) > 0) { + $ipv4 = data_get($custom_compose, 'ip.0'); + $ipv6 = data_get($custom_compose, 'ip6.0'); + data_forget($custom_compose, 'ip'); + data_forget($custom_compose, 'ip6'); + if ($ipv4 || $ipv6) { + data_forget($docker_compose['services'][$this->application->uuid], 'networks'); + } + if ($ipv4) { + $docker_compose['services'][$this->application->uuid]['networks'][$this->destination->network]['ipv4_address'] = $ipv4; + } + if ($ipv6) { + $docker_compose['services'][$this->application->uuid]['networks'][$this->destination->network]['ipv6_address'] = $ipv6; + } + $docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose); } - if ($ipv4) { - $docker_compose['services'][$this->application->uuid]['networks'][$this->destination->network]['ipv4_address'] = $ipv4; - } - if ($ipv6) { - $docker_compose['services'][$this->application->uuid]['networks'][$this->destination->network]['ipv6_address'] = $ipv6; - } - $docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose); } } else { if (count($custom_compose) > 0) { diff --git a/resources/views/livewire/project/application/advanced.blade.php b/resources/views/livewire/project/application/advanced.blade.php index ff96b228a..19cb64a05 100644 --- a/resources/views/livewire/project/application/advanced.blade.php +++ b/resources/views/livewire/project/application/advanced.blade.php @@ -31,12 +31,14 @@ helper="The deployed container will have the same name ({{ $application->uuid }}). You will lose the rolling update feature!" instantSave id="application.settings.is_consistent_container_name_enabled" label="Consistent Container Names" /> -
- - Save - + @if (!$application->settings->is_consistent_container_name_enabled) +
+ + Save + + @endif @if ($application->build_pack === 'dockercompose')

Network

Date: Tue, 2 Jul 2024 12:15:58 +0200 Subject: [PATCH 26/54] feat: token permissions feat: handle sensitive data feat: handle read-only data --- .../Api/ApplicationsController.php | 35 +++++--- .../Controllers/Api/DatabasesController.php | 25 +++++- app/Http/Controllers/Api/DeployController.php | 16 +++- app/Http/Controllers/Api/TeamController.php | 27 +++++- app/Http/Kernel.php | 2 + app/Http/Middleware/OnlyRootApiToken.php | 25 ++++++ app/Http/Middleware/ReadOnlyApiToken.php | 28 +++++++ app/Jobs/DatabaseBackupJob.php | 3 +- .../Project/Database/Clickhouse/General.php | 9 +- .../Project/Database/Dragonfly/General.php | 9 +- .../Project/Database/Keydb/General.php | 9 +- .../Project/Database/Mariadb/General.php | 9 +- .../Project/Database/Mongodb/General.php | 9 +- .../Project/Database/Mysql/General.php | 9 +- .../Project/Database/Postgresql/General.php | 9 +- .../Project/Database/Redis/General.php | 9 +- app/Livewire/Security/ApiTokens.php | 42 +++++++++- app/Models/StandaloneClickhouse.php | 13 +-- app/Models/StandaloneDragonfly.php | 9 -- app/Models/StandaloneKeydb.php | 9 -- app/Models/StandaloneMariadb.php | 9 -- app/Models/StandaloneMongodb.php | 9 -- app/Models/StandaloneMysql.php | 9 -- app/Models/StandalonePostgresql.php | 9 -- app/Models/StandaloneRedis.php | 9 -- bootstrap/helpers/api.php | 84 +++++++++++++------ .../livewire/security/api-tokens.blade.php | 58 +++++++++---- routes/api.php | 36 ++++---- 28 files changed, 328 insertions(+), 201 deletions(-) create mode 100644 app/Http/Middleware/OnlyRootApiToken.php create mode 100644 app/Http/Middleware/ReadOnlyApiToken.php diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index e37de0378..9626b0488 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -21,6 +21,27 @@ class ApplicationsController extends Controller { + private function removeSensitiveData($application) + { + $token = auth()->user()->currentAccessToken(); + if ($token->can('view:sensitive')) { + return serializeApiResponse($application); + } + $application->makeHidden([ + 'custom_labels', + 'dockerfile', + 'docker_compose', + 'docker_compose_raw', + 'manual_webhook_secret_bitbucket', + 'manual_webhook_secret_gitea', + 'manual_webhook_secret_github', + 'manual_webhook_secret_gitlab', + 'private_key_id', + ]); + + return serializeApiResponse($application); + } + public function applications(Request $request) { $teamId = getTeamIdFromToken(); @@ -32,7 +53,7 @@ public function applications(Request $request) $applications->push($projects->pluck('applications')->flatten()); $applications = $applications->flatten(); $applications = $applications->map(function ($application) { - return serializeApiResponse($application); + return $this->removeSensitiveData($application); }); return response()->json([ @@ -484,10 +505,6 @@ public function application_by_uuid(Request $request) if (! $uuid) { return response()->json(['success' => false, 'message' => 'UUID is required.'], 400); } - $return = validateIncomingRequest($request); - if ($return instanceof \Illuminate\Http\JsonResponse) { - return $return; - } $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); if (! $application) { return response()->json(['success' => false, 'message' => 'Application not found.'], 404); @@ -495,7 +512,7 @@ public function application_by_uuid(Request $request) return response()->json([ 'success' => true, - 'data' => serializeApiResponse($application), + 'data' => $this->removeSensitiveData($application), ]); } @@ -625,7 +642,7 @@ public function update_by_uuid(Request $request) return response()->json([ 'success' => true, - 'data' => serializeApiResponse($application), + 'data' => $this->removeSensitiveData($application), ]); } @@ -635,10 +652,6 @@ public function envs_by_uuid(Request $request) if (is_null($teamId)) { return invalidTokenResponse(); } - $return = validateIncomingRequest($request); - if ($return instanceof \Illuminate\Http\JsonResponse) { - return $return; - } $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); if (! $application) { diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php index 36a5fffaf..24530dca6 100644 --- a/app/Http/Controllers/Api/DatabasesController.php +++ b/app/Http/Controllers/Api/DatabasesController.php @@ -20,6 +20,27 @@ class DatabasesController extends Controller { + private function removeSensitiveData($database) + { + $token = auth()->user()->currentAccessToken(); + if ($token->can('view:sensitive')) { + return serializeApiResponse($database); + } + + $database->makeHidden([ + 'internal_db_url', + 'external_db_url', + 'postgres_password', + 'dragonfly_password', + 'redis_password', + 'mongo_initdb_root_password', + 'keydb_password', + 'clickhouse_admin_password', + ]); + + return serializeApiResponse($database); + } + public function databases(Request $request) { $teamId = getTeamIdFromToken(); @@ -32,7 +53,7 @@ public function databases(Request $request) $databases = $databases->merge($project->databases()); } $databases = $databases->map(function ($database) { - return serializeApiResponse($database); + return $this->removeSensitiveData($database); }); return response()->json([ @@ -57,7 +78,7 @@ public function database_by_uuid(Request $request) return response()->json([ 'success' => true, - 'data' => serializeApiResponse($database), + 'data' => $this->removeSensitiveData($database), ]); } diff --git a/app/Http/Controllers/Api/DeployController.php b/app/Http/Controllers/Api/DeployController.php index 76e67548c..7add22bd0 100644 --- a/app/Http/Controllers/Api/DeployController.php +++ b/app/Http/Controllers/Api/DeployController.php @@ -20,6 +20,20 @@ class DeployController extends Controller { + private function removeSensitiveData($deployment) + { + $token = auth()->user()->currentAccessToken(); + if ($token->can('view:sensitive')) { + return serializeApiResponse($deployment); + } + + $deployment->makeHidden([ + 'logs', + ]); + + return serializeApiResponse($deployment); + } + public function deployments(Request $request) { $teamId = getTeamIdFromToken(); @@ -61,7 +75,7 @@ public function deployment_by_uuid(Request $request) return response()->json([ 'success' => true, - 'data' => serializeApiResponse($deployment->makeHidden('logs')), + 'data' => $this->removeSensitiveData($deployment), ]); } diff --git a/app/Http/Controllers/Api/TeamController.php b/app/Http/Controllers/Api/TeamController.php index a256e9caf..b7837c785 100644 --- a/app/Http/Controllers/Api/TeamController.php +++ b/app/Http/Controllers/Api/TeamController.php @@ -7,17 +7,36 @@ class TeamController extends Controller { + private function removeSensitiveData($team) + { + $token = auth()->user()->currentAccessToken(); + if ($token->can('view:sensitive')) { + return serializeApiResponse($team); + } + $team->makeHidden([ + 'smtp_username', + 'smtp_password', + 'resend_api_key', + 'telegram_token', + ]); + + return serializeApiResponse($team); + } + public function teams(Request $request) { $teamId = getTeamIdFromToken(); if (is_null($teamId)) { return invalidTokenResponse(); } - $teams = auth()->user()->teams; + $teams = auth()->user()->teams->sortBy('id'); + $teams = $teams->map(function ($team) { + return $this->removeSensitiveData($team); + }); return response()->json([ 'success' => true, - 'data' => serializeApiResponse($teams), + 'data' => $teams, ]); } @@ -33,6 +52,7 @@ public function team_by_id(Request $request) if (is_null($team)) { return response()->json(['success' => false, 'message' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid'], 404); } + $team = $this->removeSensitiveData($team); return response()->json([ 'success' => true, @@ -52,10 +72,11 @@ public function members_by_id(Request $request) if (is_null($team)) { return response()->json(['success' => false, 'message' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid-members'], 404); } + $members = $team->members; return response()->json([ 'success' => true, - 'data' => serializeApiResponse($team->members), + 'data' => serializeApiResponse($members), ]); } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index e29c4a307..5f1731071 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -67,5 +67,7 @@ class Kernel extends HttpKernel 'signed' => \App\Http\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, + 'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class, + 'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class, ]; } diff --git a/app/Http/Middleware/OnlyRootApiToken.php b/app/Http/Middleware/OnlyRootApiToken.php new file mode 100644 index 000000000..bea1ec567 --- /dev/null +++ b/app/Http/Middleware/OnlyRootApiToken.php @@ -0,0 +1,25 @@ +user()->currentAccessToken(); + if ($token->can('*')) { + return $next($request); + } + + return response()->json(['success' => false, 'message' => 'You are not allowed to perform this action.'], 403); + } +} diff --git a/app/Http/Middleware/ReadOnlyApiToken.php b/app/Http/Middleware/ReadOnlyApiToken.php new file mode 100644 index 000000000..447a406a6 --- /dev/null +++ b/app/Http/Middleware/ReadOnlyApiToken.php @@ -0,0 +1,28 @@ +user()->currentAccessToken(); + if ($token->can('*')) { + return $next($request); + } + if ($token->can('read-only')) { + return response()->json(['success' => false, 'message' => 'You are not allowed to perform this action.'], 403); + } + + return $next($request); + } +} diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index 94e0ac3a3..4afe50d53 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -332,8 +332,7 @@ public function handle(): void private function backup_standalone_mongodb(string $databaseWithCollections): void { try { - ray($this->database->toArray()); - $url = $this->database->get_db_url(useInternal: true); + $url = $this->database->internal_db_url; if ($databaseWithCollections === 'all') { $commands[] = 'mkdir -p '.$this->backup_dir; if (str($this->database->image)->startsWith('mongo:4.0')) { diff --git a/app/Livewire/Project/Database/Clickhouse/General.php b/app/Livewire/Project/Database/Clickhouse/General.php index 875a36141..ffdbe95c3 100644 --- a/app/Livewire/Project/Database/Clickhouse/General.php +++ b/app/Livewire/Project/Database/Clickhouse/General.php @@ -46,10 +46,8 @@ class General extends Component public function mount() { - $this->db_url = $this->database->get_db_url(true); - if ($this->database->is_public) { - $this->db_url_public = $this->database->get_db_url(); - } + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; $this->server = data_get($this->database, 'destination.server'); } @@ -87,13 +85,12 @@ public function instantSave() return; } StartDatabaseProxy::run($this->database); - $this->db_url_public = $this->database->get_db_url(); $this->dispatch('success', 'Database is now publicly accessible.'); } else { StopDatabaseProxy::run($this->database); - $this->db_url_public = null; $this->dispatch('success', 'Database is no longer publicly accessible.'); } + $this->db_url_public = $this->database->external_db_url; $this->database->save(); } catch (\Throwable $e) { $this->database->is_public = ! $this->database->is_public; diff --git a/app/Livewire/Project/Database/Dragonfly/General.php b/app/Livewire/Project/Database/Dragonfly/General.php index d6c4eb2ce..f81f4a2f0 100644 --- a/app/Livewire/Project/Database/Dragonfly/General.php +++ b/app/Livewire/Project/Database/Dragonfly/General.php @@ -44,10 +44,8 @@ class General extends Component public function mount() { - $this->db_url = $this->database->get_db_url(true); - if ($this->database->is_public) { - $this->db_url_public = $this->database->get_db_url(); - } + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; $this->server = data_get($this->database, 'destination.server'); } @@ -102,13 +100,12 @@ public function instantSave() return; } StartDatabaseProxy::run($this->database); - $this->db_url_public = $this->database->get_db_url(); $this->dispatch('success', 'Database is now publicly accessible.'); } else { StopDatabaseProxy::run($this->database); - $this->db_url_public = null; $this->dispatch('success', 'Database is no longer publicly accessible.'); } + $this->db_url_public = $this->database->external_db_url; $this->database->save(); } catch (\Throwable $e) { $this->database->is_public = ! $this->database->is_public; diff --git a/app/Livewire/Project/Database/Keydb/General.php b/app/Livewire/Project/Database/Keydb/General.php index 381711946..2b78c9f10 100644 --- a/app/Livewire/Project/Database/Keydb/General.php +++ b/app/Livewire/Project/Database/Keydb/General.php @@ -46,10 +46,8 @@ class General extends Component public function mount() { - $this->db_url = $this->database->get_db_url(true); - if ($this->database->is_public) { - $this->db_url_public = $this->database->get_db_url(); - } + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; $this->server = data_get($this->database, 'destination.server'); } @@ -108,13 +106,12 @@ public function instantSave() return; } StartDatabaseProxy::run($this->database); - $this->db_url_public = $this->database->get_db_url(); $this->dispatch('success', 'Database is now publicly accessible.'); } else { StopDatabaseProxy::run($this->database); - $this->db_url_public = null; $this->dispatch('success', 'Database is no longer publicly accessible.'); } + $this->db_url_public = $this->database->external_db_url; $this->database->save(); } catch (\Throwable $e) { $this->database->is_public = ! $this->database->is_public; diff --git a/app/Livewire/Project/Database/Mariadb/General.php b/app/Livewire/Project/Database/Mariadb/General.php index 8b4b35d11..858d7b383 100644 --- a/app/Livewire/Project/Database/Mariadb/General.php +++ b/app/Livewire/Project/Database/Mariadb/General.php @@ -52,10 +52,8 @@ class General extends Component public function mount() { - $this->db_url = $this->database->get_db_url(true); - if ($this->database->is_public) { - $this->db_url_public = $this->database->get_db_url(); - } + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; $this->server = data_get($this->database, 'destination.server'); } @@ -114,13 +112,12 @@ public function instantSave() return; } StartDatabaseProxy::run($this->database); - $this->db_url_public = $this->database->get_db_url(); $this->dispatch('success', 'Database is now publicly accessible.'); } else { StopDatabaseProxy::run($this->database); - $this->db_url_public = null; $this->dispatch('success', 'Database is no longer publicly accessible.'); } + $this->db_url_public = $this->database->external_db_url; $this->database->save(); } catch (\Throwable $e) { $this->database->is_public = ! $this->database->is_public; diff --git a/app/Livewire/Project/Database/Mongodb/General.php b/app/Livewire/Project/Database/Mongodb/General.php index ee639ae41..5a5ef8a62 100644 --- a/app/Livewire/Project/Database/Mongodb/General.php +++ b/app/Livewire/Project/Database/Mongodb/General.php @@ -50,10 +50,8 @@ class General extends Component public function mount() { - $this->db_url = $this->database->get_db_url(true); - if ($this->database->is_public) { - $this->db_url_public = $this->database->get_db_url(); - } + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; $this->server = data_get($this->database, 'destination.server'); } @@ -115,13 +113,12 @@ public function instantSave() return; } StartDatabaseProxy::run($this->database); - $this->db_url_public = $this->database->get_db_url(); $this->dispatch('success', 'Database is now publicly accessible.'); } else { StopDatabaseProxy::run($this->database); - $this->db_url_public = null; $this->dispatch('success', 'Database is no longer publicly accessible.'); } + $this->db_url_public = $this->database->external_db_url; $this->database->save(); } catch (\Throwable $e) { $this->database->is_public = ! $this->database->is_public; diff --git a/app/Livewire/Project/Database/Mysql/General.php b/app/Livewire/Project/Database/Mysql/General.php index fc0767109..58d8e03a8 100644 --- a/app/Livewire/Project/Database/Mysql/General.php +++ b/app/Livewire/Project/Database/Mysql/General.php @@ -52,10 +52,8 @@ class General extends Component public function mount() { - $this->db_url = $this->database->get_db_url(true); - if ($this->database->is_public) { - $this->db_url_public = $this->database->get_db_url(); - } + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; $this->server = data_get($this->database, 'destination.server'); } @@ -113,13 +111,12 @@ public function instantSave() return; } StartDatabaseProxy::run($this->database); - $this->db_url_public = $this->database->get_db_url(); $this->dispatch('success', 'Database is now publicly accessible.'); } else { StopDatabaseProxy::run($this->database); - $this->db_url_public = null; $this->dispatch('success', 'Database is no longer publicly accessible.'); } + $this->db_url_public = $this->database->external_db_url; $this->database->save(); } catch (\Throwable $e) { $this->database->is_public = ! $this->database->is_public; diff --git a/app/Livewire/Project/Database/Postgresql/General.php b/app/Livewire/Project/Database/Postgresql/General.php index 1c5d39055..76bc97901 100644 --- a/app/Livewire/Project/Database/Postgresql/General.php +++ b/app/Livewire/Project/Database/Postgresql/General.php @@ -72,10 +72,8 @@ public function getListeners() public function mount() { - $this->db_url = $this->database->get_db_url(true); - if ($this->database->is_public) { - $this->db_url_public = $this->database->get_db_url(); - } + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; $this->server = data_get($this->database, 'destination.server'); } @@ -118,13 +116,12 @@ public function instantSave() return; } StartDatabaseProxy::run($this->database); - $this->db_url_public = $this->database->get_db_url(); $this->dispatch('success', 'Database is now publicly accessible.'); } else { StopDatabaseProxy::run($this->database); - $this->db_url_public = null; $this->dispatch('success', 'Database is no longer publicly accessible.'); } + $this->db_url_public = $this->database->external_db_url; $this->database->save(); } catch (\Throwable $e) { $this->database->is_public = ! $this->database->is_public; diff --git a/app/Livewire/Project/Database/Redis/General.php b/app/Livewire/Project/Database/Redis/General.php index b5c1dd881..a7ce0161a 100644 --- a/app/Livewire/Project/Database/Redis/General.php +++ b/app/Livewire/Project/Database/Redis/General.php @@ -46,10 +46,8 @@ class General extends Component public function mount() { - $this->db_url = $this->database->get_db_url(true); - if ($this->database->is_public) { - $this->db_url_public = $this->database->get_db_url(); - } + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; $this->server = data_get($this->database, 'destination.server'); } @@ -102,13 +100,12 @@ public function instantSave() return; } StartDatabaseProxy::run($this->database); - $this->db_url_public = $this->database->get_db_url(); $this->dispatch('success', 'Database is now publicly accessible.'); } else { StopDatabaseProxy::run($this->database); - $this->db_url_public = null; $this->dispatch('success', 'Database is no longer publicly accessible.'); } + $this->db_url_public = $this->database->external_db_url; $this->database->save(); } catch (\Throwable $e) { $this->database->is_public = ! $this->database->is_public; diff --git a/app/Livewire/Security/ApiTokens.php b/app/Livewire/Security/ApiTokens.php index c485a6a3a..ff8679d21 100644 --- a/app/Livewire/Security/ApiTokens.php +++ b/app/Livewire/Security/ApiTokens.php @@ -10,6 +10,12 @@ class ApiTokens extends Component public $tokens = []; + public bool $viewSensitiveData = false; + + public bool $readOnly = true; + + public array $permissions = ['read-only']; + public function render() { return view('livewire.security.api-tokens'); @@ -17,7 +23,33 @@ public function render() public function mount() { - $this->tokens = auth()->user()->tokens; + $this->tokens = auth()->user()->tokens->sortByDesc('created_at'); + } + + public function updatedViewSensitiveData() + { + if ($this->viewSensitiveData) { + $this->permissions[] = 'view:sensitive'; + $this->permissions = array_diff($this->permissions, ['*']); + } else { + $this->permissions = array_diff($this->permissions, ['view:sensitive']); + } + if (count($this->permissions) == 0) { + $this->permissions = ['*']; + } + } + + public function updatedReadOnly() + { + if ($this->readOnly) { + $this->permissions[] = 'read-only'; + $this->permissions = array_diff($this->permissions, ['*']); + } else { + $this->permissions = array_diff($this->permissions, ['read-only']); + } + if (count($this->permissions) == 0) { + $this->permissions = ['*']; + } } public function addNewToken() @@ -26,7 +58,13 @@ public function addNewToken() $this->validate([ 'description' => 'required|min:3|max:255', ]); - $token = auth()->user()->createToken($this->description); + // if ($this->viewSensitiveData) { + // $this->permissions[] = 'view:sensitive'; + // } + // if ($this->readOnly) { + // $this->permissions[] = 'read-only'; + // } + $token = auth()->user()->createToken($this->description, $this->permissions); $this->tokens = auth()->user()->tokens; session()->flash('token', $token->plainTextToken); } catch (\Exception $e) { diff --git a/app/Models/StandaloneClickhouse.php b/app/Models/StandaloneClickhouse.php index 673224650..718fc9927 100644 --- a/app/Models/StandaloneClickhouse.php +++ b/app/Models/StandaloneClickhouse.php @@ -195,7 +195,7 @@ public function type(): string protected function internalDbUrl(): Attribute { return new Attribute( - get: fn () => "clickhouse://{$this->clickhouse_user}:{$this->clickhouse_password}@{$this->uuid}:9000/{$this->clickhouse_db}", + get: fn () => "clickhouse://{$this->clickhouse_admin_user}:{$this->clickhouse_admin_password}@{$this->uuid}:9000/{$this->clickhouse_db}", ); } @@ -204,7 +204,7 @@ protected function externalDbUrl(): Attribute return new Attribute( get: function () { if ($this->is_public && $this->public_port) { - return "clickhouse://{$this->clickhouse_user}:{$this->clickhouse_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->clickhouse_db}"; + return "clickhouse://{$this->clickhouse_admin_user}:{$this->clickhouse_admin_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->clickhouse_db}"; } return null; @@ -212,15 +212,6 @@ protected function externalDbUrl(): Attribute ); } - public function get_db_url(bool $useInternal = false) - { - if ($this->is_public && ! $useInternal) { - return $this->externalDbUrl; - } else { - return $this->internalDbUrl; - } - } - public function environment() { return $this->belongsTo(Environment::class); diff --git a/app/Models/StandaloneDragonfly.php b/app/Models/StandaloneDragonfly.php index d78d656c1..b8d16d512 100644 --- a/app/Models/StandaloneDragonfly.php +++ b/app/Models/StandaloneDragonfly.php @@ -212,15 +212,6 @@ protected function externalDbUrl(): Attribute ); } - public function get_db_url(bool $useInternal = false) - { - if ($this->is_public && ! $useInternal) { - return $this->externalDbUrl; - } else { - return $this->internalDbUrl; - } - } - public function environment() { return $this->belongsTo(Environment::class); diff --git a/app/Models/StandaloneKeydb.php b/app/Models/StandaloneKeydb.php index 7b71bd55f..d2963cf02 100644 --- a/app/Models/StandaloneKeydb.php +++ b/app/Models/StandaloneKeydb.php @@ -212,15 +212,6 @@ protected function externalDbUrl(): Attribute ); } - public function get_db_url(bool $useInternal = false) - { - if ($this->is_public && ! $useInternal) { - return $this->externalDbUrl; - } else { - return $this->internalDbUrl; - } - } - public function environment() { return $this->belongsTo(Environment::class); diff --git a/app/Models/StandaloneMariadb.php b/app/Models/StandaloneMariadb.php index 00df4fe71..b7907f251 100644 --- a/app/Models/StandaloneMariadb.php +++ b/app/Models/StandaloneMariadb.php @@ -212,15 +212,6 @@ protected function externalDbUrl(): Attribute ); } - public function get_db_url(bool $useInternal = false) - { - if ($this->is_public && ! $useInternal) { - return $this->externalDbUrl; - } else { - return $this->internalDbUrl; - } - } - public function environment() { return $this->belongsTo(Environment::class); diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php index 0863522a8..0f9f9a426 100644 --- a/app/Models/StandaloneMongodb.php +++ b/app/Models/StandaloneMongodb.php @@ -232,15 +232,6 @@ protected function externalDbUrl(): Attribute ); } - public function get_db_url(bool $useInternal = false) - { - if ($this->is_public && ! $useInternal) { - return $this->externalDbUrl; - } else { - return $this->internalDbUrl; - } - } - public function environment() { return $this->belongsTo(Environment::class); diff --git a/app/Models/StandaloneMysql.php b/app/Models/StandaloneMysql.php index 79e7c37fa..bc4de88ee 100644 --- a/app/Models/StandaloneMysql.php +++ b/app/Models/StandaloneMysql.php @@ -213,15 +213,6 @@ protected function externalDbUrl(): Attribute ); } - public function get_db_url(bool $useInternal = false) - { - if ($this->is_public && ! $useInternal) { - return $this->externalDbUrl; - } else { - return $this->internalDbUrl; - } - } - public function environment() { return $this->belongsTo(Environment::class); diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php index 1d5276cf3..372d79fd8 100644 --- a/app/Models/StandalonePostgresql.php +++ b/app/Models/StandalonePostgresql.php @@ -213,15 +213,6 @@ protected function externalDbUrl(): Attribute ); } - public function get_db_url(bool $useInternal = false) - { - if ($this->is_public && ! $useInternal) { - return $this->externalDbUrl; - } else { - return $this->internalDbUrl; - } - } - public function environment() { return $this->belongsTo(Environment::class); diff --git a/app/Models/StandaloneRedis.php b/app/Models/StandaloneRedis.php index e0f863aca..64731a28b 100644 --- a/app/Models/StandaloneRedis.php +++ b/app/Models/StandaloneRedis.php @@ -208,15 +208,6 @@ protected function externalDbUrl(): Attribute ); } - public function get_db_url(bool $useInternal = false) - { - if ($this->is_public && ! $useInternal) { - return $this->externalDbUrl; - } else { - return $this->internalDbUrl; - } - } - public function environment() { return $this->belongsTo(Environment::class); diff --git a/bootstrap/helpers/api.php b/bootstrap/helpers/api.php index c5083534f..00fccda74 100644 --- a/bootstrap/helpers/api.php +++ b/bootstrap/helpers/api.php @@ -19,37 +19,67 @@ function invalidTokenResponse() function serializeApiResponse($data) { - if (! $data instanceof Collection) { - $data = collect($data); - } - $data = $data->sortKeys(); + if ($data instanceof Collection) { + $data = $data->map(function ($d) { + $d = collect($d)->sortKeys(); + $created_at = data_get($d, 'created_at'); + $updated_at = data_get($d, 'updated_at'); + if ($created_at) { + unset($d['created_at']); + $d['created_at'] = $created_at; - $created_at = data_get($data, 'created_at'); - $updated_at = data_get($data, 'updated_at'); - if ($created_at) { - unset($data['created_at']); - $data['created_at'] = $created_at; + } + if ($updated_at) { + unset($d['updated_at']); + $d['updated_at'] = $updated_at; + } + if (data_get($d, 'name')) { + $d = $d->prepend($d['name'], 'name'); + } + if (data_get($d, 'description')) { + $d = $d->prepend($d['description'], 'description'); + } + if (data_get($d, 'uuid')) { + $d = $d->prepend($d['uuid'], 'uuid'); + } - } - if ($updated_at) { - unset($data['updated_at']); - $data['updated_at'] = $updated_at; - } - if (data_get($data, 'name')) { - $data = $data->prepend($data['name'], 'name'); - } - if (data_get($data, 'description')) { - $data = $data->prepend($data['description'], 'description'); - } - if (data_get($data, 'uuid')) { - $data = $data->prepend($data['uuid'], 'uuid'); - } + if (! is_null(data_get($d, 'id'))) { + $d = $d->prepend($d['id'], 'id'); + } - if (data_get($data, 'id')) { - $data = $data->prepend($data['id'], 'id'); - } + return $d; + }); - return $data; + return $data; + } else { + $d = collect($data)->sortKeys(); + $created_at = data_get($d, 'created_at'); + $updated_at = data_get($d, 'updated_at'); + if ($created_at) { + unset($d['created_at']); + $d['created_at'] = $created_at; + + } + if ($updated_at) { + unset($d['updated_at']); + $d['updated_at'] = $updated_at; + } + if (data_get($d, 'name')) { + $d = $d->prepend($d['name'], 'name'); + } + if (data_get($d, 'description')) { + $d = $d->prepend($d['description'], 'description'); + } + if (data_get($d, 'uuid')) { + $d = $d->prepend($d['uuid'], 'uuid'); + } + + if (! is_null(data_get($d, 'id'))) { + $d = $d->prepend($d['id'], 'id'); + } + + return $d; + } } function sharedDataApplications() diff --git a/resources/views/livewire/security/api-tokens.blade.php b/resources/views/livewire/security/api-tokens.blade.php index b9120878d..3a5d4560c 100644 --- a/resources/views/livewire/security/api-tokens.blade.php +++ b/resources/views/livewire/security/api-tokens.blade.php @@ -3,29 +3,59 @@ API Tokens | Coolify -
-

API Tokens

- +
+

API Tokens

+
Tokens are created with the current team as scope. You will only have access to this team's resources. +
-

Create New Token

-
- - Create New Token +

New Token

+ +
+ + Create New Token +
+
+ Permissions : +
+ @if ($permissions) + @foreach ($permissions as $permission) + @if ($permission === '*') +
All (root/admin access), be careful!
+ @else +
{{ $permission }}
+ @endif + @endforeach + @endif +
+
+

Token Permissions

+
+ + +
@if (session()->has('token')) -
Please copy this token now. For your security, it won't be shown again. +
Please copy this token now. For your security, it won't be shown + again.
{{ session('token') }}
@endif -

Issued Tokens

+

Issued Tokens

@forelse ($tokens as $token) -
-
-
{{ $token->name }}
+
+
Description: {{ $token->name }}
+
Last used: {{ $token->last_used_at ? $token->last_used_at->diffForHumans() : 'Never' }}
+
+ @if ($token->abilities) + Abilities: + @foreach ($token->abilities as $ability) +
{{ $ability }}
+ @endforeach + @endif
+ Revoke token diff --git a/routes/api.php b/routes/api.php index 69eead3ba..61b3348b7 100644 --- a/routes/api.php +++ b/routes/api.php @@ -10,6 +10,8 @@ use App\Http\Controllers\Api\ServersController; use App\Http\Controllers\Api\TeamController; use App\Http\Middleware\ApiAllowed; +use App\Http\Middleware\OnlyRootApiToken; +use App\Http\Middleware\ReadOnlyApiToken; use App\Models\InstanceSettings; use Illuminate\Http\Request; use Illuminate\Support\Facades\Http; @@ -31,7 +33,7 @@ }); Route::group([ - 'middleware' => ['auth:sanctum'], + 'middleware' => ['auth:sanctum', OnlyRootApiToken::class], 'prefix' => 'v1', ], function () { Route::get('/enable', function () { @@ -81,13 +83,13 @@ Route::get('/projects/{uuid}/{environment_name}', [ProjectController::class, 'environment_details']); Route::get('/security/keys', [SecurityController::class, 'keys']); - Route::post('/security/keys', [SecurityController::class, 'create_key']); + Route::post('/security/keys', [SecurityController::class, 'create_key'])->middleware([ReadOnlyApiToken::class]); Route::get('/security/keys/{uuid}', [SecurityController::class, 'key_by_uuid']); - Route::patch('/security/keys/{uuid}', [SecurityController::class, 'update_key']); - Route::delete('/security/keys/{uuid}', [SecurityController::class, 'delete_key']); + Route::patch('/security/keys/{uuid}', [SecurityController::class, 'update_key'])->middleware([ReadOnlyApiToken::class]); + Route::delete('/security/keys/{uuid}', [SecurityController::class, 'delete_key'])->middleware([ReadOnlyApiToken::class]); - Route::match(['get', 'post'], '/deploy', [DeployController::class, 'deploy']); + Route::match(['get', 'post'], '/deploy', [DeployController::class, 'deploy'])->middleware([ReadOnlyApiToken::class]); Route::get('/deployments', [DeployController::class, 'deployments']); Route::get('/deployments/{uuid}', [DeployController::class, 'deployment_by_uuid']); @@ -99,29 +101,29 @@ Route::get('/resources', [ResourcesController::class, 'resources']); Route::get('/applications', [ApplicationsController::class, 'applications']); - Route::post('/applications', [ApplicationsController::class, 'create_application']); + Route::post('/applications', [ApplicationsController::class, 'create_application'])->middleware([ReadOnlyApiToken::class]); Route::get('/applications/{uuid}', [ApplicationsController::class, 'application_by_uuid']); - Route::patch('/applications/{uuid}', [ApplicationsController::class, 'update_by_uuid']); - Route::delete('/applications/{uuid}', [ApplicationsController::class, 'delete_by_uuid']); + Route::patch('/applications/{uuid}', [ApplicationsController::class, 'update_by_uuid'])->middleware([ReadOnlyApiToken::class]); + Route::delete('/applications/{uuid}', [ApplicationsController::class, 'delete_by_uuid'])->middleware([ReadOnlyApiToken::class]); Route::get('/applications/{uuid}/envs', [ApplicationsController::class, 'envs_by_uuid']); - Route::post('/applications/{uuid}/envs', [ApplicationsController::class, 'create_env']); - Route::post('/applications/{uuid}/envs/bulk', [ApplicationsController::class, 'create_bulk_envs']); + Route::post('/applications/{uuid}/envs', [ApplicationsController::class, 'create_env'])->middleware([ReadOnlyApiToken::class]); + Route::post('/applications/{uuid}/envs/bulk', [ApplicationsController::class, 'create_bulk_envs'])->middleware([ReadOnlyApiToken::class]); Route::patch('/applications/{uuid}/envs', [ApplicationsController::class, 'update_env_by_uuid']); - Route::delete('/applications/{uuid}/envs/{env_uuid}', [ApplicationsController::class, 'delete_env_by_uuid']); + Route::delete('/applications/{uuid}/envs/{env_uuid}', [ApplicationsController::class, 'delete_env_by_uuid'])->middleware([ReadOnlyApiToken::class]); - Route::match(['get', 'post'], '/applications/{uuid}/action/deploy', [ApplicationsController::class, 'action_deploy']); - Route::match(['get', 'post'], '/applications/{uuid}/action/restart', [ApplicationsController::class, 'action_restart']); - Route::match(['get', 'post'], '/applications/{uuid}/action/stop', [ApplicationsController::class, 'action_stop']); + Route::match(['get', 'post'], '/applications/{uuid}/action/deploy', [ApplicationsController::class, 'action_deploy'])->middleware([ReadOnlyApiToken::class]); + Route::match(['get', 'post'], '/applications/{uuid}/action/restart', [ApplicationsController::class, 'action_restart'])->middleware([ReadOnlyApiToken::class]); + Route::match(['get', 'post'], '/applications/{uuid}/action/stop', [ApplicationsController::class, 'action_stop'])->middleware([ReadOnlyApiToken::class]); Route::get('/databases', [DatabasesController::class, 'databases']); - Route::post('/databases', [DatabasesController::class, 'create_database']); + Route::post('/databases', [DatabasesController::class, 'create_database'])->middleware([ReadOnlyApiToken::class]); Route::get('/databases/{uuid}', [DatabasesController::class, 'database_by_uuid']); // Route::patch('/databases/{uuid}', [DatabasesController::class, 'update_by_uuid']); - Route::delete('/databases/{uuid}', [DatabasesController::class, 'delete_by_uuid']); + Route::delete('/databases/{uuid}', [DatabasesController::class, 'delete_by_uuid'])->middleware([ReadOnlyApiToken::class]); - Route::delete('/envs/{env_uuid}', [EnvironmentVariablesController::class, 'delete_env_by_uuid']); + Route::delete('/envs/{env_uuid}', [EnvironmentVariablesController::class, 'delete_env_by_uuid'])->middleware([ReadOnlyApiToken::class]); }); From 3c13f1ff61e4b9791e440d86483ad06d36d0463d Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 2 Jul 2024 13:39:44 +0200 Subject: [PATCH 27/54] feat: restart database feat: public dbs stay public after restart feat: patch database conf --- app/Actions/Database/RestartDatabase.php | 29 ++ app/Actions/Database/StartDatabase.php | 57 +++ app/Actions/Database/StopDatabase.php | 2 - app/Actions/Database/StopDatabaseProxy.php | 1 - .../Controllers/Api/DatabasesController.php | 331 ++++++++++++++++-- app/Http/Controllers/Api/DeployController.php | 91 ++--- app/Livewire/Project/Database/Heading.php | 44 +-- .../Project/Database/Postgresql/General.php | 8 - bootstrap/helpers/api.php | 8 + bootstrap/helpers/databases.php | 17 +- .../project/database/heading.blade.php | 20 +- routes/api.php | 3 +- 12 files changed, 471 insertions(+), 140 deletions(-) create mode 100644 app/Actions/Database/RestartDatabase.php create mode 100644 app/Actions/Database/StartDatabase.php diff --git a/app/Actions/Database/RestartDatabase.php b/app/Actions/Database/RestartDatabase.php new file mode 100644 index 000000000..0400d924d --- /dev/null +++ b/app/Actions/Database/RestartDatabase.php @@ -0,0 +1,29 @@ +destination->server; + if (! $server->isFunctional()) { + return 'Server is not functional'; + } + StopDatabase::run($database); + + return StartDatabase::run($database); + } +} diff --git a/app/Actions/Database/StartDatabase.php b/app/Actions/Database/StartDatabase.php new file mode 100644 index 000000000..323c52ff9 --- /dev/null +++ b/app/Actions/Database/StartDatabase.php @@ -0,0 +1,57 @@ +destination->server; + if (! $server->isFunctional()) { + return 'Server is not functional'; + } + switch ($database->getMorphClass()) { + case 'App\Models\StandalonePostgresql': + $activity = StartPostgresql::run($database); + break; + case 'App\Models\StandaloneRedis': + $activity = StartRedis::run($database); + break; + case 'App\Models\StandaloneMongodb': + $activity = StartMongodb::run($database); + break; + case 'App\Models\StandaloneMysql': + $activity = StartMysql::run($database); + break; + case 'App\Models\StandaloneMariadb': + $activity = StartMariadb::run($database); + break; + case 'App\Models\StandaloneKeydb': + $activity = StartKeydb::run($database); + break; + case 'App\Models\StandaloneDragonfly': + $activity = StartDragonfly::run($database); + break; + case 'App\Models\StandaloneClickhouse': + $activity = StartClickhouse::run($database); + break; + } + if ($database->is_public && $database->public_port) { + StartDatabaseProxy::dispatch($database); + } + + return $activity; + } +} diff --git a/app/Actions/Database/StopDatabase.php b/app/Actions/Database/StopDatabase.php index 66a32e811..e4903ff35 100644 --- a/app/Actions/Database/StopDatabase.php +++ b/app/Actions/Database/StopDatabase.php @@ -29,7 +29,5 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St if ($database->is_public) { StopDatabaseProxy::run($database); } - // TODO: make notification for services - // $database->environment->project->team->notify(new StatusChanged($database)); } } diff --git a/app/Actions/Database/StopDatabaseProxy.php b/app/Actions/Database/StopDatabaseProxy.php index 1b262c898..b2092e2ef 100644 --- a/app/Actions/Database/StopDatabaseProxy.php +++ b/app/Actions/Database/StopDatabaseProxy.php @@ -27,7 +27,6 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St $server = data_get($database, 'service.server'); } instant_remote_process(["docker rm -f {$uuid}-proxy"], $server); - $database->is_public = false; $database->save(); DatabaseStatusChanged::dispatch(); } diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php index 24530dca6..1a5106d7c 100644 --- a/app/Http/Controllers/Api/DatabasesController.php +++ b/app/Http/Controllers/Api/DatabasesController.php @@ -2,15 +2,10 @@ namespace App\Http\Controllers\Api; -use App\Actions\Database\StartClickhouse; -use App\Actions\Database\StartDragonfly; -use App\Actions\Database\StartKeydb; -use App\Actions\Database\StartMariadb; -use App\Actions\Database\StartMongodb; -use App\Actions\Database\StartMysql; -use App\Actions\Database\StartPostgresql; -use App\Actions\Database\StartRedis; +use App\Actions\Database\StartDatabase; +use App\Actions\Database\StartDatabaseProxy; use App\Actions\Database\StopDatabase; +use App\Actions\Database\StopDatabaseProxy; use App\Enums\NewDatabaseTypes; use App\Http\Controllers\Controller; use App\Models\Project; @@ -82,9 +77,114 @@ public function database_by_uuid(Request $request) ]); } + public function update_by_uuid(Request $request) + { + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_init_database', 'mysql_root_password', 'mysql_user', 'mysql_database', 'mysql_conf']; + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + $return = validateIncomingRequest($request); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } + $validator = customApiValidator($request->all(), [ + 'name' => 'string|max:255', + 'description' => 'string|nullable', + 'image' => 'string', + 'is_public' => 'boolean', + 'public_port' => 'numeric|nullable', + 'limits_memory' => 'string', + 'limits_memory_swap' => 'string', + 'limits_memory_swappiness' => 'numeric', + 'limits_memory_reservation' => 'string', + 'limits_cpus' => 'string', + 'limits_cpuset' => 'string|nullable', + 'limits_cpu_shares' => 'numeric', + 'postgres_user' => 'string', + 'postgres_password' => 'string', + 'postgres_db' => 'string', + 'postgres_initdb_args' => 'string', + 'postgres_host_auth_method' => 'string', + 'postgres_conf' => 'string', + 'clickhouse_admin_user' => 'string', + 'clickhouse_admin_password' => 'string', + 'dragonfly_password' => 'string', + 'redis_password' => 'string', + 'redis_conf' => 'string', + 'keydb_password' => 'string', + 'keydb_conf' => 'string', + 'mariadb_conf' => 'string', + 'mariadb_root_password' => 'string', + 'mariadb_user' => 'string', + 'mariadb_password' => 'string', + 'mariadb_database' => 'string', + 'mongo_conf' => 'string', + 'mongo_initdb_root_username' => 'string', + 'mongo_initdb_root_password' => 'string', + 'mongo_initdb_init_database' => 'string', + 'mysql_root_password' => 'string', + 'mysql_user' => 'string', + 'mysql_database' => 'string', + 'mysql_conf' => 'string', + ]); + + $extraFields = array_diff(array_keys($request->all()), $allowedFields); + if ($validator->fails() || ! empty($extraFields)) { + $errors = $validator->errors(); + if (! empty($extraFields)) { + foreach ($extraFields as $field) { + $errors->add($field, 'This field is not allowed.'); + } + } + + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + $uuid = $request->uuid; + removeUnnecessaryFieldsFromRequest($request); + $database = queryDatabaseByUuidWithinTeam($uuid, $teamId); + if (! $database) { + return response()->json(['success' => false, 'message' => 'Database not found.'], 404); + } + if ($request->is_public && $request->public_port) { + if (isPublicPortAlreadyUsed($database->destination->server, $request->public_port, $database->id)) { + return response()->json(['success' => false, 'message' => 'Public port already used by another database.'], 400); + } + } + + $whatToDoWithDatabaseProxy = null; + if ($request->is_public === false && $database->is_public === true) { + $whatToDoWithDatabaseProxy = 'stop'; + } + if ($request->is_public === true && $request->public_port && $database->is_public === false) { + $whatToDoWithDatabaseProxy = 'start'; + } + + $database->update($request->all()); + + if ($whatToDoWithDatabaseProxy === 'start') { + StartDatabaseProxy::dispatch($database); + } elseif ($whatToDoWithDatabaseProxy === 'stop') { + StopDatabaseProxy::dispatch($database); + } + + return response()->json([ + 'success' => true, + 'message' => 'Database updated.', + 'data' => $this->removeSensitiveData($database), + ]); + + } + public function create_database(Request $request) { - $allowedFields = ['type', 'name', 'description', 'image', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'postgres_user', 'postgres_password', 'postgres_db', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares']; + $allowedFields = ['type', 'name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_init_database', 'mysql_root_password', 'mysql_user', 'mysql_database', 'mysql_conf']; + $teamId = getTeamIdFromToken(); if (is_null($teamId)) { return invalidTokenResponse(); @@ -103,9 +203,8 @@ public function create_database(Request $request) 'environment_name' => 'string|required', 'server_uuid' => 'string|required', 'destination_uuid' => 'string', - 'postgres_user' => 'string', - 'postgres_password' => 'string', - 'postgres_db' => 'string', + 'is_public' => 'boolean', + 'public_port' => 'numeric|nullable', 'limits_memory' => 'string', 'limits_memory_swap' => 'string', 'limits_memory_swappiness' => 'numeric', @@ -113,6 +212,32 @@ public function create_database(Request $request) 'limits_cpus' => 'string', 'limits_cpuset' => 'string|nullable', 'limits_cpu_shares' => 'numeric', + 'postgres_user' => 'string', + 'postgres_password' => 'string', + 'postgres_db' => 'string', + 'postgres_initdb_args' => 'string', + 'postgres_host_auth_method' => 'string', + 'postgres_conf' => 'string', + 'clickhouse_admin_user' => 'string', + 'clickhouse_admin_password' => 'string', + 'dragonfly_password' => 'string', + 'redis_password' => 'string', + 'redis_conf' => 'string', + 'keydb_password' => 'string', + 'keydb_conf' => 'string', + 'mariadb_conf' => 'string', + 'mariadb_root_password' => 'string', + 'mariadb_user' => 'string', + 'mariadb_password' => 'string', + 'mariadb_database' => 'string', + 'mongo_conf' => 'string', + 'mongo_initdb_root_username' => 'string', + 'mongo_initdb_root_password' => 'string', + 'mongo_initdb_init_database' => 'string', + 'mysql_root_password' => 'string', + 'mysql_user' => 'string', + 'mysql_database' => 'string', + 'mysql_conf' => 'string', 'instant_deploy' => 'boolean', ]); @@ -133,7 +258,9 @@ public function create_database(Request $request) } $serverUuid = $request->server_uuid; $instantDeploy = $request->instant_deploy ?? false; - + if ($request->is_public && ! $request->public_port) { + $request->offsetSet('is_public', false); + } $project = Project::whereTeamId($teamId)->whereUuid($request->project_uuid)->first(); if (! $project) { return response()->json(['succes' => false, 'message' => 'Project not found.'], 404); @@ -154,12 +281,41 @@ public function create_database(Request $request) return response()->json(['success' => false, 'message' => 'Server has multiple destinations and you do not set destination_uuid.'], 400); } $destination = $destinations->first(); - + if ($request->has('public_port') && $request->is_public) { + if (isPublicPortAlreadyUsed($server, $request->public_port)) { + return response()->json(['success' => false, 'message' => 'Public port already used by another database.'], 400); + } + } if ($request->type === NewDatabaseTypes::POSTGRESQL->value) { removeUnnecessaryFieldsFromRequest($request); + if ($request->has('postgres_conf')) { + if (! isBase64Encoded($request->postgres_conf)) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => [ + 'postgres_conf' => 'The postgres_conf should be base64 encoded.', + ], + ], 422); + } + $postgresConf = base64_decode($request->postgres_conf); + if (mb_detect_encoding($postgresConf, 'ASCII', true) === false) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => [ + 'postgres_conf' => 'The postgres_conf should be base64 encoded.', + ], + ], 422); + } + $request->offsetSet('postgres_conf', $postgresConf); + } $database = create_standalone_postgresql($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartPostgresql::dispatch($database); + StartDatabase::dispatch($database); + if ($request->is_public && $request->public_port) { + StartDatabaseProxy::dispatch($database); + } } return response()->json([ @@ -169,9 +325,34 @@ public function create_database(Request $request) ]); } elseif ($request->type === NewDatabaseTypes::MARIADB->value) { removeUnnecessaryFieldsFromRequest($request); + if ($request->has('mariadb_conf')) { + if (! isBase64Encoded($request->mariadb_conf)) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => [ + 'mariadb_conf' => 'The mariadb_conf should be base64 encoded.', + ], + ], 422); + } + $mariadbConf = base64_decode($request->mariadb_conf); + if (mb_detect_encoding($mariadbConf, 'ASCII', true) === false) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => [ + 'mariadb_conf' => 'The mariadb_conf should be base64 encoded.', + ], + ], 422); + } + $request->offsetSet('mariadb_conf', $mariadbConf); + } $database = create_standalone_mariadb($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartMariadb::dispatch($database); + StartDatabase::dispatch($database); + if ($request->is_public && $request->public_port) { + StartDatabaseProxy::dispatch($database); + } } return response()->json([ @@ -181,9 +362,34 @@ public function create_database(Request $request) ]); } elseif ($request->type === NewDatabaseTypes::MYSQL->value) { removeUnnecessaryFieldsFromRequest($request); + if ($request->has('mysql_conf')) { + if (! isBase64Encoded($request->mysql_conf)) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => [ + 'mysql_conf' => 'The mysql_conf should be base64 encoded.', + ], + ], 422); + } + $mysqlConf = base64_decode($request->mysql_conf); + if (mb_detect_encoding($mysqlConf, 'ASCII', true) === false) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => [ + 'mysql_conf' => 'The mysql_conf should be base64 encoded.', + ], + ], 422); + } + $request->offsetSet('mysql_conf', $mysqlConf); + } $database = create_standalone_mysql($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartMysql::dispatch($database); + StartDatabase::dispatch($database); + if ($request->is_public && $request->public_port) { + StartDatabaseProxy::dispatch($database); + } } return response()->json([ @@ -193,9 +399,34 @@ public function create_database(Request $request) ]); } elseif ($request->type === NewDatabaseTypes::REDIS->value) { removeUnnecessaryFieldsFromRequest($request); + if ($request->has('redis_conf')) { + if (! isBase64Encoded($request->redis_conf)) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => [ + 'redis_conf' => 'The redis_conf should be base64 encoded.', + ], + ], 422); + } + $redisConf = base64_decode($request->redis_conf); + if (mb_detect_encoding($redisConf, 'ASCII', true) === false) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => [ + 'redis_conf' => 'The redis_conf should be base64 encoded.', + ], + ], 422); + } + $request->offsetSet('redis_conf', $redisConf); + } $database = create_standalone_redis($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartRedis::dispatch($database); + StartDatabase::dispatch($database); + if ($request->is_public && $request->public_port) { + StartDatabaseProxy::dispatch($database); + } } return response()->json([ @@ -207,7 +438,10 @@ public function create_database(Request $request) removeUnnecessaryFieldsFromRequest($request); $database = create_standalone_dragonfly($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartDragonfly::dispatch($database); + StartDatabase::dispatch($database); + if ($request->is_public && $request->public_port) { + StartDatabaseProxy::dispatch($database); + } } return response()->json([ @@ -217,9 +451,34 @@ public function create_database(Request $request) ]); } elseif ($request->type === NewDatabaseTypes::KEYDB->value) { removeUnnecessaryFieldsFromRequest($request); + if ($request->has('keydb_conf')) { + if (! isBase64Encoded($request->keydb_conf)) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => [ + 'keydb_conf' => 'The keydb_conf should be base64 encoded.', + ], + ], 422); + } + $keydbConf = base64_decode($request->keydb_conf); + if (mb_detect_encoding($keydbConf, 'ASCII', true) === false) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => [ + 'keydb_conf' => 'The keydb_conf should be base64 encoded.', + ], + ], 422); + } + $request->offsetSet('keydb_conf', $keydbConf); + } $database = create_standalone_keydb($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartKeydb::dispatch($database); + StartDatabase::dispatch($database); + if ($request->is_public && $request->public_port) { + StartDatabaseProxy::dispatch($database); + } } return response()->json([ @@ -231,7 +490,10 @@ public function create_database(Request $request) removeUnnecessaryFieldsFromRequest($request); $database = create_standalone_clickhouse($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartClickhouse::dispatch($database); + StartDatabase::dispatch($database); + if ($request->is_public && $request->public_port) { + StartDatabaseProxy::dispatch($database); + } } return response()->json([ @@ -241,9 +503,34 @@ public function create_database(Request $request) ]); } elseif ($request->type === NewDatabaseTypes::MONGODB->value) { removeUnnecessaryFieldsFromRequest($request); + if ($request->has('mongo_conf')) { + if (! isBase64Encoded($request->mongo_conf)) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => [ + 'mongo_conf' => 'The mongo_conf should be base64 encoded.', + ], + ], 422); + } + $mongoConf = base64_decode($request->mongo_conf); + if (mb_detect_encoding($mongoConf, 'ASCII', true) === false) { + return response()->json([ + 'success' => false, + 'message' => 'Validation failed.', + 'errors' => [ + 'mongo_conf' => 'The mongo_conf should be base64 encoded.', + ], + ], 422); + } + $request->offsetSet('mongo_conf', $mongoConf); + } $database = create_standalone_mongodb($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartMongodb::dispatch($database); + StartDatabase::dispatch($database); + if ($request->is_public && $request->public_port) { + StartDatabaseProxy::dispatch($database); + } } return response()->json([ diff --git a/app/Http/Controllers/Api/DeployController.php b/app/Http/Controllers/Api/DeployController.php index 7add22bd0..79d98df0c 100644 --- a/app/Http/Controllers/Api/DeployController.php +++ b/app/Http/Controllers/Api/DeployController.php @@ -2,14 +2,7 @@ namespace App\Http\Controllers\Api; -use App\Actions\Database\StartClickhouse; -use App\Actions\Database\StartDragonfly; -use App\Actions\Database\StartKeydb; -use App\Actions\Database\StartMariadb; -use App\Actions\Database\StartMongodb; -use App\Actions\Database\StartMysql; -use App\Actions\Database\StartPostgresql; -use App\Actions\Database\StartRedis; +use App\Actions\Database\StartDatabase; use App\Actions\Service\StartService; use App\Http\Controllers\Controller; use App\Models\ApplicationDeploymentQueue; @@ -192,66 +185,28 @@ public function deploy_resource($resource, bool $force = false): array if (gettype($resource) !== 'object') { return ['success' => false, 'message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid]; } - $type = $resource?->getMorphClass(); - if ($type === 'App\Models\Application') { - $deployment_uuid = new Cuid2(7); - queue_application_deployment( - application: $resource, - deployment_uuid: $deployment_uuid, - force_rebuild: $force, - ); - $message = "Application {$resource->name} deployment queued."; - } elseif ($type === 'App\Models\StandalonePostgresql') { - StartPostgresql::run($resource); - $resource->update([ - 'started_at' => now(), - ]); - $message = "Database {$resource->name} started."; - } elseif ($type === 'App\Models\StandaloneRedis') { - StartRedis::run($resource); - $resource->update([ - 'started_at' => now(), - ]); - $message = "Database {$resource->name} started."; - } elseif ($type === 'App\Models\StandaloneKeydb') { - StartKeydb::run($resource); - $resource->update([ - 'started_at' => now(), - ]); - $message = "Database {$resource->name} started."; - } elseif ($type === 'App\Models\StandaloneDragonfly') { - StartDragonfly::run($resource); - $resource->update([ - 'started_at' => now(), - ]); - $message = "Database {$resource->name} started."; - } elseif ($type === 'App\Models\StandaloneClickhouse') { - StartClickhouse::run($resource); - $resource->update([ - 'started_at' => now(), - ]); - $message = "Database {$resource->name} started."; - } elseif ($type === 'App\Models\StandaloneMongodb') { - StartMongodb::run($resource); - $resource->update([ - 'started_at' => now(), - ]); - $message = "Database {$resource->name} started."; - } elseif ($type === 'App\Models\StandaloneMysql') { - StartMysql::run($resource); - $resource->update([ - 'started_at' => now(), - ]); - $message = "Database {$resource->name} started."; - } elseif ($type === 'App\Models\StandaloneMariadb') { - StartMariadb::run($resource); - $resource->update([ - 'started_at' => now(), - ]); - $message = "Database {$resource->name} started."; - } elseif ($type === 'App\Models\Service') { - StartService::run($resource); - $message = "Service {$resource->name} started. It could take a while, be patient."; + switch ($resource?->getMorphClass()) { + case 'App\Models\Application': + $deployment_uuid = new Cuid2(7); + queue_application_deployment( + application: $resource, + deployment_uuid: $deployment_uuid, + force_rebuild: $force, + ); + $message = "Application {$resource->name} deployment queued."; + break; + case 'App\Models\Service': + StartService::run($resource); + $message = "Service {$resource->name} started. It could take a while, be patient."; + break; + default: + // Database resource + StartDatabase::dispatch($resource); + $resource->update([ + 'started_at' => now(), + ]); + $message = "Database {$resource->name} started."; + break; } return ['success' => true, 'message' => $message, 'deployment_uuid' => $deployment_uuid]; diff --git a/app/Livewire/Project/Database/Heading.php b/app/Livewire/Project/Database/Heading.php index ae88ac12b..6435f6781 100644 --- a/app/Livewire/Project/Database/Heading.php +++ b/app/Livewire/Project/Database/Heading.php @@ -2,14 +2,8 @@ namespace App\Livewire\Project\Database; -use App\Actions\Database\StartClickhouse; -use App\Actions\Database\StartDragonfly; -use App\Actions\Database\StartKeydb; -use App\Actions\Database\StartMariadb; -use App\Actions\Database\StartMongodb; -use App\Actions\Database\StartMysql; -use App\Actions\Database\StartPostgresql; -use App\Actions\Database\StartRedis; +use App\Actions\Database\RestartDatabase; +use App\Actions\Database\StartDatabase; use App\Actions\Database\StopDatabase; use App\Actions\Docker\GetContainersStatus; use Livewire\Component; @@ -47,7 +41,6 @@ public function activityFinished() public function check_status($showNotification = false) { GetContainersStatus::run($this->database->destination->server); - // dispatch_sync(new ContainerStatusJob($this->database->destination->server)); $this->database->refresh(); if ($showNotification) { $this->dispatch('success', 'Database status updated.'); @@ -67,32 +60,15 @@ public function stop() $this->check_status(); } + public function restart() + { + $activity = RestartDatabase::run($this->database); + $this->dispatch('activityMonitor', $activity->id); + } + public function start() { - if ($this->database->type() === 'standalone-postgresql') { - $activity = StartPostgresql::run($this->database); - $this->dispatch('activityMonitor', $activity->id); - } elseif ($this->database->type() === 'standalone-redis') { - $activity = StartRedis::run($this->database); - $this->dispatch('activityMonitor', $activity->id); - } elseif ($this->database->type() === 'standalone-mongodb') { - $activity = StartMongodb::run($this->database); - $this->dispatch('activityMonitor', $activity->id); - } elseif ($this->database->type() === 'standalone-mysql') { - $activity = StartMysql::run($this->database); - $this->dispatch('activityMonitor', $activity->id); - } elseif ($this->database->type() === 'standalone-mariadb') { - $activity = StartMariadb::run($this->database); - $this->dispatch('activityMonitor', $activity->id); - } elseif ($this->database->type() === 'standalone-keydb') { - $activity = StartKeydb::run($this->database); - $this->dispatch('activityMonitor', $activity->id); - } elseif ($this->database->type() === 'standalone-dragonfly') { - $activity = StartDragonfly::run($this->database); - $this->dispatch('activityMonitor', $activity->id); - } elseif ($this->database->type() === 'standalone-clickhouse') { - $activity = StartClickhouse::run($this->database); - $this->dispatch('activityMonitor', $activity->id); - } + $activity = StartDatabase::run($this->database); + $this->dispatch('activityMonitor', $activity->id); } } diff --git a/app/Livewire/Project/Database/Postgresql/General.php b/app/Livewire/Project/Database/Postgresql/General.php index 76bc97901..eabbbd679 100644 --- a/app/Livewire/Project/Database/Postgresql/General.php +++ b/app/Livewire/Project/Database/Postgresql/General.php @@ -27,10 +27,7 @@ class General extends Component public function getListeners() { - $userId = auth()->user()->id; - return [ - "echo-private:user.{$userId},DatabaseStatusChanged" => 'database_stopped', 'refresh', 'save_init_script', 'delete_init_script', @@ -77,11 +74,6 @@ public function mount() $this->server = data_get($this->database, 'destination.server'); } - public function database_stopped() - { - $this->dispatch('success', 'Database proxy stopped. Database is no longer publicly accessible.'); - } - public function instantSaveAdvanced() { try { diff --git a/bootstrap/helpers/api.php b/bootstrap/helpers/api.php index 00fccda74..e4da4a563 100644 --- a/bootstrap/helpers/api.php +++ b/bootstrap/helpers/api.php @@ -151,6 +151,14 @@ function validateIncomingRequest(Request $request) 'error' => 'Invalid JSON.', ], 400); } + // check if valid json is empty + if (empty($request->json()->all())) { + return response()->json([ + 'success' => false, + 'message' => 'Invalid request.', + 'error' => 'Empty JSON.', + ], 400); + } } function removeUnnecessaryFieldsFromRequest(Request $request) diff --git a/bootstrap/helpers/databases.php b/bootstrap/helpers/databases.php index ef3f8ac9b..089298956 100644 --- a/bootstrap/helpers/databases.php +++ b/bootstrap/helpers/databases.php @@ -178,9 +178,6 @@ function create_standalone_clickhouse($environment_id, $destination_uuid, ?array return $database; } -/** - * Delete file locally on the filesystem. - */ function delete_backup_locally(?string $filename, Server $server): void { if (empty($filename)) { @@ -188,3 +185,17 @@ function delete_backup_locally(?string $filename, Server $server): void } instant_remote_process(["rm -f \"{$filename}\""], $server, throwError: false); } + +function isPublicPortAlreadyUsed(Server $server, int $port, ?string $id = null): bool +{ + if ($id) { + $foundDatabase = $server->databases()->where('public_port', $port)->where('is_public', true)->where('id', '!=', $id)->first(); + } else { + $foundDatabase = $server->databases()->where('public_port', $port)->where('is_public', true)->first(); + } + if ($foundDatabase) { + return true; + } + + return false; +} diff --git a/resources/views/livewire/project/database/heading.blade.php b/resources/views/livewire/project/database/heading.blade.php index 0c0647324..cee1f0520 100644 --- a/resources/views/livewire/project/database/heading.blade.php +++ b/resources/views/livewire/project/database/heading.blade.php @@ -7,7 +7,8 @@