From a29e4b4a1897b354aa9b7abc7cc01f6d840a8126 Mon Sep 17 00:00:00 2001 From: Daniel Esteves Date: Fri, 19 Jun 2026 17:54:52 -0400 Subject: [PATCH 1/3] feat(blueprint): add engram template Engram Cloud is a self-hosted persistent-memory sync server for AI coding agents. Deploys the published ghcr.io/gentleman-programming/engram image with a PostgreSQL backend, auto-generated auth secrets (cloud token, admin token, JWT secret), and a domain on the dashboard/API port. --- blueprints/engram/docker-compose.yml | 37 +++++++++++++++++++++++++++ blueprints/engram/engram.png | Bin 0 -> 31632 bytes blueprints/engram/template.toml | 22 ++++++++++++++++ meta.json | 17 ++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 blueprints/engram/docker-compose.yml create mode 100644 blueprints/engram/engram.png create mode 100644 blueprints/engram/template.toml diff --git a/blueprints/engram/docker-compose.yml b/blueprints/engram/docker-compose.yml new file mode 100644 index 00000000..6e72b17a --- /dev/null +++ b/blueprints/engram/docker-compose.yml @@ -0,0 +1,37 @@ +version: "3.8" +services: + postgres: + image: postgres:16-alpine + restart: unless-stopped + environment: + POSTGRES_USER: engram + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: engram_cloud + volumes: + - engram-postgres:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U engram -d engram_cloud"] + interval: 5s + timeout: 3s + retries: 10 + + engram: + image: ghcr.io/gentleman-programming/engram:1.16.3 + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + environment: + ENGRAM_DATABASE_URL: ${ENGRAM_DATABASE_URL} + ENGRAM_JWT_SECRET: ${ENGRAM_JWT_SECRET} + ENGRAM_CLOUD_TOKEN: ${ENGRAM_CLOUD_TOKEN} + ENGRAM_CLOUD_ADMIN: ${ENGRAM_CLOUD_ADMIN} + ENGRAM_CLOUD_ALLOWED_PROJECTS: ${ENGRAM_CLOUD_ALLOWED_PROJECTS} + ENGRAM_CLOUD_HOST: "0.0.0.0" + ENGRAM_PORT: "8080" + command: ["cloud", "serve"] + expose: + - 8080 + +volumes: + engram-postgres: diff --git a/blueprints/engram/engram.png b/blueprints/engram/engram.png new file mode 100644 index 0000000000000000000000000000000000000000..26e90f3f42f46b0204c6fdb38c50446b6412422e GIT binary patch literal 31632 zcmcG0Lz6CC&-K-|ZQHhO+xBUkwr$(SY1_7K+qS#E`x(4H;Tt4bJINrGN+x?%B|=eN z0uCA*8UO&mNlA(-0{}q(K|nkt@PFD8hZ(v z1OSCu0YLwk@}FS;CjbCQJ`e!pKMV9fUq0~vMS%eMApeH}@&8Nosk`|C00aS2qC%?f zKvzCs*?OxUr|oZ;CvYq_#Ym0fhEl0?Sf;6F(TDL#wwtWm$-cc;E$j46a&C)uLGYvU z#-sI94Z~Bc*yf>OkLe88zgrYBW}}XYKNogOGs}xvJg@j~Gj9C+yIdle%x=?0hVUSN zG1CY8aKTR)8N&j&V5k1SxO(BhPnbJ??e2bV3s#SWQH^GJXL@-@xuttb$V!yX5u&p@PANF{2b$u4!o z3Yp7THNqKQpvhQ*He5-D5?wrV-#5+N`RnSi!^#h^WlJ>@8?bd{qx-uYE!vGG4rPWC z|Ir>7;P4pn=*Ra?L2ul4fHlTQo%_2<kmgAiY2CcY@0BjzHOVyG)G@&!TL zf3P!?;Q<<$k1h%t{4cW4;wE#j&wi+HT8M%x5Zvf7xX}vIGZ9t9@KF;B-&IL=QdLui zLd92H2|9vEq=|666iX`5fY$=-TO9cg%!r83u+CDQM+20GpSX~}f= zISWAik|i?vIQp_vE5xfHEHeEhA&I|WD$M5n`>g8c@1(HmMQoIju;gjHi<*H&|1@DXX?%oO(0H#eq~KfhqyvQ z_WQH$cM<2s2F^Z*3$gXY`jG&?_qr_(czz6KLYe`duxS66*?=s9WFHtoptJx*tbr*# z(s)Cg5XHJ^{1<7|Dd-;SESOd43Oq{#da{Ch6qJApZhI-_w}+=c{HqT_up_JQ9(vXZ zT5B5J$NV_9WG0d-k3w((iacXwN&K%?5vlKS|LpckL-XgsF%hzYkxMjDMFpYM&1=r2 zy)|^lc);)XGmv0&dz{jt`Wj7zm<4-Q3!)Lwt>+Vr-`!YI+$wS-V7d>h4waBmRjlpG z13qn1u$nXzLv<{xBKjRzE-u|9AS+v5fcehPk4-Svy#B+6bc$rUb%RSp!5w-5H~3FZ zB&@#OEv#&)78g;;q!vrkv?-?Dp7m)x%P502kR>>%td4Y+3?7(Ift15wHri7{!rb)s z-EFfasDtDW6A}1Cx}_7&q>dI79oYkcq$QRq zM8H7-JtG$-2~s9H>nI2ZN4YQS-0zPyAUe&dtY@06Mk@T?WV26a_UvY?)owC``q*^z#eS~F}D!nZvNhf zuw%d6rx_*b>ZmsLwX>RjOsf6be*8@)mJT%09g<9*qTtv;i3!f@_9QIn9Oy;)KK1Vg zzqCg<&>hc02l>s!1}p{TBg5$uJLa9->^c-#JoFWP>miA4F0D42DW2vXRmmh6`{WTSmdY%_DSA_{FLU_%0U^#Mr?PUGkbL56gS}|QB#{bgmHW-~>Tdcm zKO*PGUVD4f-ce(%YXMy09m3i-f_>HqyVzZ8*O5(s76oL)WVXS z=)l}!;d~!;x0A_yVX(jE9M6Y9%cUCU;v20<3Zqz3v#wkME2l0RbgDDQ%offCS+AtR z${G{SRf&sB>*JV2NP`_sJ=14v(-v08Vwt1V4)e5KBy1!utB+wWjK$ODM>N_)xi@62 zD_i}}AN}t0_`P`9Qq{u^ndORsCE*4_$90b$eP9KFN{i#ZS!t2O@=FoT!H$S0_k-$* zcQTdCXh|(t0a8hoX5_7d=>zXQEBvS&$^G`Ud4ta}3jD<%pBKK8<6D)whi$)fn|XG3 zt1eHMxwGoo-5J;1uSW)ezTT<>S^$CX<35N7!5zWQ(&ug=GaC^r_$HFjM@d!5x_VQO z`F>`K&4jg$J4uzZ^lnu}J#Cg0IXQTkLJr^Gm2Dnm1e0oN@rHax#LF<-o$3BCeZu1F z%y@|*1vY!R_rdYIsZ&=06+{zH;6V%?hF&BBWN2t^VH5VoZUN?qV#|)VXBuokwbE?x zAF`rM=+1K(u)iha`eY{<$HzL==<1ea9%b5ieos~({IhU zl-<9KMcKYV4BE$`pIctMo%*D&*cm(RU_5v?Hp@?*AAEMfKfU{zcHz`2+TrX8I8U`S z5gQzqJ6eF6U0s{d7Dqh)^`9EP{Dm{V>Rmvq{tF*N?P@OA^9RU=?@dSjSIq0`Zm*8h z3)CO}I@6jy+sR(JcLZ8*lVOC;uy)!xS+;&LS(xaWoM)QW-PT207r5qEXBX#aPjd4E zBdH`&`6_2rx~x^*RwFS-k>$CR%G#^`ZVIYuUrJ!n>V=7$f zgdQB7nYhJ|YU$BUOzwEU0j}2}Gr^?-*l1%xe5&ZqzrZUc)^~l@0z5$U5;qCbm8htS z46a!#y=J%V_r*a4bnGFEBcu-+>d=HWE%MFzEx+&aK*-*mCO0;525JJTPjA2d-w#Fj zYOXORjkflLKX;c`U&l8>s_mD0rQPZQJ%UC<4QFmQ&(9a`5RuEhE-G+j&NgrYE*XJ& zPZP~@D8iIJr@o!&IEyx0ijgaG{oOlaH#s|!H3wH9-K5%!Sn^@QVezhVuXNj*^lkz= z1RPv-_q-eqOQIpV)i^d6WL8uc{1?@G6cfhKPt@%_f2R#TbLqfgQuRH>Hr~9H(!9!h z^SZzB><#JrblFh~g40{kSA4#1!e~+%czb`}_6NhfSaPuvBNiU9Q30Syo+0hdGWT6D zB-dHg>N}6+6q=pxd(Er*oNoC#(5LPIbeMlHAPh}{SpIeFa%);$|Hx`fhtj%F{k|}o zXGcYmwbXB5V!B+8hjdUhD3(lvO&vs`J=O9x{#eEaI>tl=0~>2wF?73KugJMw3;H+T z3VXTPYz{4%aw1`%vgfg-u0u!3MA|-=3@;xi-Hf}@=WsHu^OO%Cf?hf@3w}G`=K~fI zVCgW`IyB@cfH7Ps0ctm1sUuGsPcmgr+~tBHKk+R^0%b!$dAZV&I8BHhfk0U$-+H*IIomZzwP}0D#1Yl50ZQjCn6@- z2X1W$D8;2VU|K-*Z;3JBm1;5?Zh_9-^dZK8fadxEl01_nBQCKM7Wf_;`a16itG!;P zcNeGu5qWaRP|7Gx;pad}QF~)LcfFlUT zCVtZovD3sll>{t>NP8{6Fz-LAFyuk{zoPpVQ!;y?hSwXrm z9mhc=YM>Y))-rpHn^xnmfMl*9q)*fKrK|!1w3Vc;k&TRu%I@TyT|KAe?njQ>Q~BMTo9>tuk@Y zl#}-H$!S*YktJh-zv@Yc8LSJMbmmE8bLl`IGNz65ved(U6hLmewPT1+#z>-J^PqgN z$D!zZO`;3qZOJnWNye(qbk^B&jM{SIxt?E5ty`oEI+?8DzfVH3Cw=Vs)zK9rDpdZa zjrnFBpTK7SJj5&loO0mb*@PIq5JstRWk{QC(WlRs#QpSXWbK_VB%Cln^T&-CN$LW^ z>NJQb2O~sVfWr}zU#yO z_qA8x;`W&tt8=8fx+=}IgpCos?}sjioD`xd7&q3}_faJFppfJB%Hw7)H_Yt4CytMa ziwiC5N^}MoO7jL$*!9KQw26f&)iNoKLA&8| zMvo<|F|4CT8XMvhqAg>%8=hsE$W=Uh_L$FS_6DuWrsZ!!4GiLF1%4bo zvvv!umd+{lv8tyNxOLzI%k+Sn+;ldmKaoy^DH=j^?l~q7D6lhg5y_ZdZwAmDJ#~Rq#asZS zUhmnyd`)t?>k_SAi^t-l!RHB)-zp=c;23)S$?Wf99tFt0J>P2%i#H>=#RI2Fy}7Q% zKb`O6xFlnV!+8{nz@*xo9UcDKlK?M#OFviALNhZt@Va5mU_C|tWfwC+R|Fkp#zJuLSzda~JkPU35F@wQBhq1%mw2V2xnI>U6m-Wm;psY) z`3t$(EB?=2zqiLj`_$DXdTJ*u&?{y^SQ$pHY-k%yZbv506DcTJ%?ZORU-rkZaSk^d zHv!jC5EBOXBrJk_pbX;`Ac}AR-kG@+eBNr$d`^sm!uI!^Q!CpA%=t$qI;hDRFe?OR z2^S-XZ-H>!4C*+`8g87OC7##0q|k5K3Lod+a*fcwWzxkj=|)TM_qEMPSumE5;kmg~ zEs{=k`%V3}Apz#l!2!ZAXza?ZM;-1CZ9dBkA~Nwwy~~!L{m|_Re~b!r_Yn_iMT{nq zCz)QCz|v9(v|C0l=?ZJv$`dnq=hBO8zdA>aJK5F&M#NSraOsf0)Z|ZcPzRX@{+Rw> zg(h|DkJ{)}A7vUnyZr2^E6kE2pay4!aBwPci7?5l?U4e%V=AeSzF@}4l!{9OkqeO? zE`z^cYSe`R@_fP7mDtmz_Xb;aofBG^n79!2GF7@dDhB$>_syrhRAwrw7zQX89V=v2 zLMB;NA$}isiylVE=^6d^iiw$8D-Gns6Wu03$j=CWGp{yezrn%t9ZW`?)uP6riZf#{ zA7WZ~gT^PIN+R1#M)8VnbvfDG$s|!TDAa?vb`Ol{#K{NDx#-9&nAdHCDS?+#iCEfy zX>S?HMVD32AZ@HSv%>H{X7da*R?c#+KtB?(;rIh5h`4qnlm%;ljPfTvlX~1gqAz3#|D{k|Skh4f)&>8$Almj}c^It5%!wo@iE zI(PDNfU;@|UA=C(-VzABjobG;49b`UHGm)ckD~H;VsVS37`0Swr(b0F-Vf*HYf*bb zHmcIDKXJ^mqH9QOi)aC>iI@TL?^SvpXfq$WpReiQ1?BY7jQK>ZxzFbL9ef=ue=_8S zDhba1j2VH*S=r*7yiJutNH?Yu_`Y7^Ck8T&AeSpLZ%%;^Q714bB}H(roUev40SNuC zf{CEofq8oTF>!Fkq8@0nplM*LT%!ybX}mq8me*H(nR*L|c-U^(&B#F9h?)4mO9ara zUZCjlAj1+9L9-A>95i}(Ut;V#qDO?7wz0A)>FK915e1K3ZM*fj7+l3mL%;D`GyR<* z)QAGG{0{`^{S#1o}Vz#89iy8~PU|5QVfWjd-^nLIa$Ve~$^^qaNQ)lwG}i^#VZv^nis+8Au)**A2w zKP61H{Yx-cS;qEaBbm$q8Suy35(!j35 z1F}jD4mI`Dac^!U1q-q@F4R@-x;{iz3Vs0U6@oh>l=J5A|gh7 z8&va#`*$Uq;fvD2H2Fqs7Xq-WTq0qn!lso%m*Hi%7Td|z4tbx;_DzD_?&JH<#WVbH` zce{$KdXhOzlC+tVfQ*DNKvq|!BDD)khQ9V1Haj6YJB=3^!S_xLH4#P3`w1Tc6EH-9 z&MV#=z=as^gsa!<|E`iq+n+7Hqo|OkQto*q?RS;&z;6bPDTzCJQRv9J#NcQ+Kf8o3 zK(U71*arJm86FBQY~tiepSSOZ42#J`T~O3MNlw=YZ2TM@Da3wT9*8pFgxm!Q7IZ)G zj*ZoS%vu=)Ry~R6=La*-ij~0oHekG7yVz{dX}yl0H5^NU zp17i=mc%H#c!wBv+RukFRO&L$Bh0y}g&Gv%hh5avPDz49klc_%u+aL7yMC_W=<1bw zDryPD$7 zY=LniY}TiIh9}Xt8e$x1gByc-8B=4~$fzBrme^cOR?xAZ?$tL&%Q;WiD#<*kj5}+m zR?)iE{;jVkM({mN9=%4LqOYtLccIs7tvIXSgow=*xP93xj$m0o|5)8&4qdMN#v{-2 znGZbC;jpuTX6<%xzO~wg+!K8Qf$>X+5`=dr?B46eMul(8TFE(K_Ub=Dvyr31q`pI< zqyQP%Is*FI=rYz3+Ak;IyERfyV{@ak(H_zgB!JR;Y7o6KJ<7ZBI!+4tZyBz4Zm+)I zow>m`3KxeGX+=n8;@1Hn9)atuz>X1{7=Lir-1b8vicn;sFQ9fT)QrvxKRkF@NtSC> zzkngfPz6Ckpb~mKmow)|<||F`NMsMH*xwyfO*zp<6f)r@Cn*j>!6qZ4;lnMpQXDt@ ztEd}FC9BY7%5+9O2i8%+y#jM)uKdm;;0&}C75Vt^1C9zUFho?WqLYy9H=CNTpTz!s zpaykUs4^bfC0k;YdZmTjXm&WvA04Xzs4U9;fdp&U9%JIcwNo*1BV~;QB)T47yV@^d z5cJzT&!-$(fl&ew6%UhS{xD24d}ZD8;Sh$4o~siZkpfR*c?KRpYuxDcmKYFB0Z^+| z^Ng>h`iz2jDlmMmGG;q?!g_zUS~nZ?)71r`8I}98A|oRWJa5Wc2%t4kl6*1+O8fhf z3y5XKm91tBrZ`)?pA{szFFTJDWmpv|wG6LJ{XTiUB9cU4{rU3c3A@xd&aN@9);R5t zR}`+CxO6WV@;ZZ;HS@)j0{F|uRn?W3RJC_`?SlNnR8~fceA%ni)K#Be;WNSuGV5<_wOw{eI{V(PA>`xYV9TbY`*Nqh4&x9*y%u+? zGl#^4cV_TfSH1TOl78O_Hb9cHaf!tpS+RNQUUL>|c7&Ly(btpuRc$6XTrP&KOAVEy z7+H?q%4^{CH1K^6#uauyH4jP%vg72(;}eptn1uQWj)8F%?N&v{a7!$6)#u=?C=pTbsEk$c_6?&vuJC+}(Wof9Sm)Ly~8s@006t>}%+U_?e+yr=G z-0YH}6oGIyx58&D78MHaKYES#qL*BEG;h#@{b1`vR1L)ysu}=EKFa9)cb$UD=`ajF z&r=Z)0o){I$G+Yb{l_!CPJKT!1s!v2OmNjbVgLYlSk3)s^@`)k>+ty!(_bFyiSsHf zWo2PB*VDSgR&{?-0an*U#oVj}H%i zCV1D|5fpc{H^CVpC=QA6Y@iR{7!P@bn27b6r$8huU1| zKc?%)j|v$YaxV#Kti_oNG6lbL1d`K_ywCMVemX0AN**qjbjVR|Et=?n%KLg--NrgG zWSPSCWYUJ4a6d(Oa}sD_7kEGWw$rC{bQiVM<>b`QZ+<^!wXyQKS1LJ2lxBu&uk%j< z&x>;ffhMERw;v1@YI8_hK?*3%(!aHqoO59QM2A5V(&3hdJQnm_PwF(nS{9K6UE8 z&<&vq>L8f$fH9;YA}M>3@}gLr*T3drv`d)N-j~*0ej##1{bFK*FsQlES|HfKL11}2 zstXhE?x8phO%(}BxIP(Y*1G#WD=7*Ck50X3f4X_*k;=skPp|#LXC3w4Z7_kX4lJs` zMsDRqY7!H`}w==YHF6*vI1aU{4@qzRkCz6vh0?roD% z9j(yL8ntM|q2n$;bO7~6N;nDP6=&b^SK_yQ5@R4qCY845wLtH<1nwKdbJyeYxo3`Y z222=NBtAmqgcKQVnr5LOICw3r^np6hs6h%mU}BF~V_a5hJ+RJfby1=Jm+StZ(k^k+ z{4e2X*{p!R1?5$O&v2>tz~8wnAn_f)k8t;cv$k}o2Sugi_(IJwmVbKMjN~PwVV}}^ zAcsUrFx@b{!1JP%+c81^h=%@@Gv7QmJN8`UGrKD%c12$xQiXvf1%)BOhZ^lwDGu zZI~~Z>rt}u?zic*w#$IwL0xoAm0&aU#7xIuR>CqM#VcB{dl7+nSzID7{U_5T#(c(e zL*9J+-5%3o(E@P3H^i<$NqAP6j(ouUPI-wE{n{$6{`afS z?(98O&C z-G|vaOOYg0Uc-cLhU#J-+7ZFmM;rr|hzFP$aW;p3=VW{6EOlztZsPuRHb^I0ps;HmFFzym9yQf; zEhQd7lyIvF6gs)L>WUAiaA}Y)UBZS|-JVHI7S8SN!s9|efx(X7*5=^&L^SrgQ@xkX zWT^_n>j-2P3HCpod>mHz^&qOH<3jF}oSSrPL>%f>#}F#DP=#4zqHy{GX0YYA zJc1AQA>M3=7;0Z?W2T(=e6;N4KYv?7#Ot3}#{UUi@_b}?pB~b^ejnY0Dm5525eqn)RqatT1sE#%I z)rO~=i;&Ogam@VWjR%!0`b;4D>SqBt*O%d;V8jqZZGLbolEg~Qy0lZ%g!#EJXz2;h0?g}Q3W zQhz282`W)|ii=b_YHwSV%ZNsdm)LP68Zx@l=I-m*S$^G#O+Lsl-;K|0VxZJ}Ti68j;SeZ&HOW1sls4_oUT{N3O8ofx)4D`8UuqNSNIC}H`~M?h<#%z@zGg75_1x`A6j zhU8MrLV6%ot*bS)x^2H_I6>!xZFu5HZHQgrUk;+FxxQ~nIB(c!p*^q&iB1^W^47|v z=ybZ8t?Hp~t3}Rr31`%pg*s-puzJBg3*U%+(b^eURYS_WiYPF4H=12N*AFF*Y>{ki znAOvAFiVmbd-Ei2*PdfWO`gn3M{wCsuD?p&j}0FoH@T0r3*jy$Y2vJzCnX>x&V(_g z!aL^eN4H@l^U_}sv0aUykF&56TLMtjSXKbrD$oPUKA=qXV8FJWtR9EvXImN zn3<&i#>?mD4j5LDdO`+daYu(Y6Z6Pwv+Bx;RfdGI8Nb3pSp=hj}~fD%3aJg zr4<52p=SM}qlBC5*sJDKlN3`N4WKG+JFg1Kdtsk_xiSjD$ekim57KVL6G7=D4qcaDorxZYac5~BEf{EXt*81FUjuj)$;+KrixqmwXw-GRH z#YUuQ?LfmMC{G4T6jhCuGIO8--xYgCYN=?r_FTE@N&hTgY*8%eWVHNs|Fc*J$}MSl zB)z(VMwd@tRRgdMk84VN1oBr4ckG$acm7=IgfRFPSX@s}Q-Uvpn6~eBIqw}s_P#kzy?iwrYRDnfV*Suxx6+;6z4wrq7zg*^o>V!%LWM_tm}LXHk-gLD55*Omh)8h!6g)fPUDI#k?Y!wWB4~fxuF= zL-Y>o$;U~*osUK&a;a)N)Sv`^Vpff8k%06};HExaVw;X@ArZAPa?0v*fo0c|;!Zpj z@P~n;3%x$Du;38~Mg`gx?pv*vWo)9GI)pxxXi3eDoh4>&K3_7ffJ6s&>Kro~=nDjw zJP1E=?osVj`dtBc@`4E!Qpcv(0hCJtwHys>_*fMJ5PYx|tnxv)n`4I{79$J{4qF?` zcdJ)^PKs^PD*{etVPZ>YqGsYEm(>%VoQ4EXcvfU-t?&=k4*4E_uG^n4LGU(qCP4}I zEHp0731HOLWm9;|i(Ec0>6vV9rY)CDcX5&geIMp&T6M_>RUOAI3|4(?NH~RYU<=iH z4rK8N=at?1@`iJiPX6}a91{i?{8Y`AOje>HIHJHUOaY2xOXn-bCV;6K>~1ER|Z<)UUn5i)FDjN3tMctb$J9@8U-w#n@6RRj-i{XYK3*Dk z9X?Ui=d~-bH5l}@J{0n5zIJ|S{5E-r6`ti{$;s}B8VuSU|C*DoBV2BiP$(xXS)|&v z*}v8p{0#3BnU>GVl0h5Z09P@}-vLna6^cUzj}8{V->CEHwh2qH0nX((__}sNWNCAV z#-U3PS%htW{FlW<2vn=gG1te!CB$d#^87|y64`3y2*NOs44X{)eMbxOKA6*eW!Ymo z{E3ZD=G(&x#?D$0L!^T;lEo8&7Q!G~tINsoV#izud6gs51Puvs&Ld?BhaoRgLUU2) z__Tx0Wdl~f5r;U;8pj)4`!`~Py-1nE@yj!6P*VAki5X!N8Ey<3j(bXV;Co+OS#vcyyx#9@R6rH>aBVKrYG>jc#DnVl_Ne8ck7F~vWp-qzf*5~~uZ~1Oc4A;O z-12ZG_6WA1?t#Py+TxLcRlrTo&ac-rTh0P|UnA`a1x*=}g0%y1>k&_ChO-{nGUdXL zlqz`lLzVFTS>4^7{1ra3e0>N(6m<~YfnKNHfIVo~X^*1FM2rbhV9iB*WfpJ|T;io@ zCkp`ll+n$ijf>orrZ2v~r(m^s>5!jcxYnHhSKepRz@^^StD~PaPq$VA=1SA0O^oE9 z9nBe_hAIgxCbbrm(S=1>8OpQ=H^)5mUk`Jrd3RCLnC-`sCBwDchP@vK=vKfBh2JQYfN)lOL8#I5HuOedHAD%P@zTm#%4j5ZZ0SH*DkLz zl^+xqJH!~H&@Of-a3@gK$N^_=D`6Z2$vt6XCE&>LP@#Td&#t@%ltHtkb;dMpofcRS z5SWeVmVBzuPG|rJHa%R&C?DhhmBmI^8^N6M#c<`;|wnoBo1a59dQIL1>qdKkjJ#>sD_i!ON ztXagIz~_Xva8T$dMt!8;c6hD4?vs_LuX56Y z*bfuCP}##fHH<5$U?{vbR*8spnKic#tr=5nXep3dxcIjjv2)m#x$+XL>*JtbBl_5<7a*hQD`kpHAOu~m@_PtB6w+0-ci}3lA z%t97PFmAMd#u~w13jOO<*`F&+k|c8s)w0WU;v1HkhNS}DcjEj$F#JBk=q5FKyO?30?PP`ip=V9;umjVDTyAz9kfS3VE>e42b8P(27vQ;!gmB0GXA z3%2zUNTR>ZhzT*m#p}l(d$4=?3VOYq@e`Uh1V+-|Q9usUB=KcW z+*WM8bkB2aX)?`Y^^L_#VEnPss>^;XnYre3;^Op?v_=zpTC6RxGU_l zWKB*oS1az$&anIzo_^@W*m>~2+=P4+d``i#Rk&V5JIB6!h^wj84O4@Jg&+ofEBi>= zn;kb&@AH4VzY!zYaX;2Y2hB31UD4A0?;8UaHYSxrZ5l;S(;lIKDmRK`Ds&iLj-F$q zMU`WghE~M+>Opch8TmCMxl+qdVkJ+mSfKm4-)ZLJ02aZ%^Y*OZkdgR-`Q$J(pz0r% zM^X57tEAX|yqvI6n`|hHwYkoB#mmd&AUbB7Bo~Q!XpiGDh8X-@LtN{`6-+=HR27Vn({nxt>@X+xB$AwogOXUBr0HZQg7#ICCM^bMA= z53&k2PFi5lp(8UR)crx*(9KSZ+i$n6#vx5^tjza_-fN41N`c&tGm}O#v6Aq-6GhV_ zv9$>R_~crKGct}|VJ4^s?FlMvSNT=~!Xusvyb;Jez*M9o$lC=RP-$aD6_tD?Z4nW! zSh#K7%9HJyHJN)`$CJa(!~ZT{Mz-71Rcz#|9|C1Dk&dVec0W%QlqzD0&cQ;;gqqcH z_Q$Kq+bg9UQWassKg1uWOUxr(|1Mz;kJkMuHmp640z0}?#pReyCOai&W&zkjD0lF@ z>2&?t15J^RRZ%!Y=r53UVzsHDs&6PEX%HoibA8mevza| zExWO?>{8b-KP%&$+_!7MBB7bMmhx9oz*uxie;{-ETIUbU2k>Z3_RSP1y;1!t~K2@rq_XOWpbNB3~ zs;|v-q40`FM^Q6zR`2XuKXkSfBR1??x)E?BI04wT!ggEu zw*7uh$P+j@-hy_D3Lq@wuKPU)8n|MhZpG8SZeow&rKWgYt%V~db8(3gL+CvQs$;FI zIpsz1^9RY(iiVNJVgjt|F`>S|`tWGhet$Z^mqvk^>+Y&8-9RK7 z^Iab};<&R^WF)tL?zN)!69vtUp-x>dPg)z66|_E0 zh#gdEzZVTFyM4U$3{L)c{pYr+Q+YXLxn*D=GH@aCYoX%nL0iLPVC?#mr&eOlN%C<#NW&9FKb}_5j<%}jbA`v?`&dRLgR$XUkQp3ZJS5!YW?!n1YVHg% zqI=GxZbrAO)R|uwlD0=BC_Xk89?=YzJiF5bTgu&}4E)!8kRmIa>~*Qe-Sm39OyBcU z)D?Fab?BrVHD51KI{#V>Td2fk*R6XO6c~ZIi8+)tD;)uKV zl2Q{_cDjCu6N7~|2217?Dq1V$xKbo7J7nXKBe08dCk0>(6I;xKf@Ng^I+PFL!ul3f zeN=`a?Q+x>)|--%m@g(iP7Xor=IEl8q9xaYLMeHJlAI)v1LRK3sH?cKj;QwzH1%HR zKp7QNH_F^?8XmnEUg))3haA3KL~o0nr1Kc0Fqc2 zhTZKo$*s@96rWpzd6o3LU8c)@=iPf)Qz*qw>iu_!QGKFwHl$bIBPFZ0v~sdx$ps7I zel>n7mE(Pko6?!LskkH}r($8@xL;ZkB?Hn2h!vwKBNB;_uy_3hyfYsT_o?AsZp3(V zI)w)LnwVVq+p*_8AvGOjFZC~HkhzEB9tsA%e!xqip>wGIOK$M@M3;^P+{bDN4Oa{ff*# zMXy6CT(5mAj1HMqq?mKha`Ag{vx4&iwD(BU`mt9`e$V=#i_#tRk!KlbUSvN-#nRbB zr|v!zWMx#G62dA`3UifYM&pV(D)pu(px9+fa>R|#NvGFJ0^)5ZU{7#t`4k1fvO*R9 z?Z{7@%c8FvtRKI-=(EprFa;m!vxd0bGo`vLDASSV#}%aDqTPz76#}Oqo+p) z3ZaFMVj>`bNk2l%aKg9}osY1HLF?X>`*%jB=T&IEE^S=9WnDxWX4Vz^b8s-8P~fjU z_n~x7GYfs)930coV9?Ve*->M)FD*w~SJWu}CYp7LuaT)jB zIs{+G(zMgBcbc{w?Gv)cnQ4wPU2K1&8ir53?VG`5mrI=E)utUI-LUXn%vcf`&dOP< zEigC>A!nMbR+oB{$w%j_$0@sfv89F3*p7IH{)R?U-<;>laH z_wj?)&mGfa`}DX1!9Ky0I3$W9)tjk37k4&GQ?98sFONY}!UR1o7r<<5)bh4DOhz`4 z;~4#^)#A#9g$1Pu07aCFSZ^0?-b0PRwGn8Qq-_jNuhgi`HrWclC7X=9WhVs^Vxy`Gzvj3hb zzBc}HWC~^yEysn6?Ij~o#)O;{)~6E$Fp}6OE;^}39ooAfMJYjrnq7UNLSAXB`MZ}8 z67dJ1{pskLIEcDHv_=|dl>(m!?!;Po3KXMnfo_LJ2>Hwy`=?1#j&A{kN zvwtBo5jHx=2nDE0RPD0Hy}NL<^dUdfbP(CS;MgjQy99 zEG&gS1Io|J8#njh(ujXy7C5w5mh;&1hg#3gzCV<&$0Qw65+*j}2c>S*PZrLs%O`3q z@H1+)x1u~KLTaON=aY^0!g6^$SXu3c?gO|fFoC=hnEIdMM|J13J3M+ZIl5A46QMLF z%fxy4B+msqIn9E-Y;Iq&Fk;un-v^Vx`+Bm?-5F~ejG8a4S0UQe{>q_X+w9gQr1(6( zZ+S@roPbf_(Zs^QOwS2b;<~>(C#~HUB+RZ(<#q`PJirB^8{)9gh-b+aWx{>T=yk0! z_zPd~Bk6K-uzB+6QlUW$dz=x0$bF@2o{{&4w3w*0*u@9Ljwy+7nG4MXK1Bw%%z)t- zew2TTw&Nm0Cd>yQW*81D>RfgZL?}SWnNdXhEYLU9pf;J+R@s*%<8oS?A6*q@%`gnE zEzMfCiy3dI1>(RfiTjWnPzzM1Bn_@qX3AYB?kF}!`Aiq!i)SQA(nqr5)qfSaDIRdtfyLrzbKu?xc9Oi^i_d&7yRpY4oV};=U6HV*;Ej znaqDChxEO^ifCN5(WoH9)x(a`Vsc_Bj5{GUgh3!GEG5F{+o>s(aTJP{@d8|VvSA9N zAoz}q0sE^^JUHyRyj}%7*TeLu{n53<^+y4LaIB4i7T7W}qE};N;dTa{b&8S81V{Zj zkNQ?rx<8utsiZ&j@I~~l(}VM_g8WCUPHNgd+cHrYTN-Q3rs>67@~h}`L_I`=ePNK^ zfy9h-h+OXAhsup6t5KxJJe9}+qT`O z?y7Cu_Nr~$wr$(CZ5yk0yWj6;?9+LeF=jGy=gOVix~=Lm-UNJET+iAknM4ts%;;Iv z8RdQsgZ^l*C5cd#-13dv88GCpSz|EiRJWCt&d)J5Om=N|mN&wxhb!7Fo(rgE+2n~< zYeK&jx zsc4L*j4=G#$u_n+ohI)U4!!>TTgP^+Jm0;2R+gb$bWP?VHYe2*)NYXn)jq}H5aR^Vck+BbP-jjuzzA6tD%demi3?4YU=^JVp~++f zm$XVUw)rX#sqtet$#}HjX_A_106rW;DFwqS@rg4{2xngIC)EbtdUqPY5Iong2Adha zroXb4&Q2XJDHW?Z-3(3O>O4Y@+?3lB7=<-r83wtf{s#5WKs+l&8G{MsM@t6L1ARGg zNlD9a^NfSp-h#)iz-va8@FU)C$^wJ+L#E*@uOuT&g~7dhf&13#bKo>pxD z5iYxmA_O)ar5IZ{QK-=$kx1}@ZXrP*6%4C80a6?$jr&inoC~pk)OM0edtE54=B#RQ zKCR|VGTY4^PgXHjzN0`M9m)~__02c~+tIevDats&|C;wrWMI1BB1$tgrfQK1dZqlh zvCX`rsv?wrn9e|HzbNaV^a!Ko0y932O)?s-zJwDSaQ;=IMi&E~(q9Q^(B;K1X+_QPjQ*lwsp=znbKP$JMOg z%dpxhTEzUO(mJT%!oddJK`;AO>#&mACt@hn0trdL*LJz%?v)9s zruG-IhG-m$v3L3)Q-3XdNyKc~L`C{U{*?g#ZvVRVHhM2?g)GCR-Y8>Y-GZ@luQfnd zOY{EA;j_MASW{wgL4+iNzS6kp$ZbYj)qR^O*4dN_md2rcPC&FU*I%LnIC#9Ve(Jzk z(W4C1P#J!dl}&o0p^1Zmo!9z z{cia`Nn+CTQ=)K`o2dypc=+?QSSSIZx$K~?{=L+S3EIJjb$1%H_-`a8B*%`#cUSB6 z$)D!4x5bEpm+@qy!+#u8p{10J5M#11KUPW15{KG1kA(?D-WXs8Kok^M8HSibyciHS zCJQ)=-vz5q?c!z0l8)!AW5punOmbJP+3Hd5k~EnQcMXw(E&R;w1_m-CTL zF2Ybz#-fZRIg=(xm$A%Bi=@OW1loG@M7vgGD&>-CW}Sj*@s0T`?Nkb<1`tS*wpogi z3pJWdKq&PNSfQC2V3^}jvZ33SPhm9Y$jYaRMWpuWb;7Y=U54~3GZw-FiuJ&}zi(Qf z&2D7IMN+W~%tvUBsv$cl{Zd19Isy{F4RRr6po)b11zHgdAC>Xg?P*0t27=!TCtMkDxrGMV~3WA87p2k+0;6Ao>U2ByTI$*i{)pvvT)CO z41^s#Idiul1iUy2!al!!@PBBU1piqSZW|+I0qRu2)eHF6y2v~bV zVmccNXsgk1yEE8Cji(;`3g)uCcsz&$Qm_EFhMF85xs>AZiXk}K-%*)HWegc6S)^#g z5}p-{6gF#-**RK?P~&)K1)SwlA!!&_WCQdh!BsIqwd_8}AA#W;aa+P1AQ@~^f1#$D zhIa4%y9qO+=qZpr18n`5<^M>H9qGA`Vi92L8TuTf+=gZwrxS2-2LSb3dgwuE^LjLf z@H|o{l4Uw4EV?m7gaDjztW&TIWx|IdYZxS1;xd3#2I`_K{fQW*|M-BdgaoaSI@P%f zv!}-w*l(Q|lpEBcNOK%>8O+tNbjh_~9!XA^4$>T$cmijYh!vSCLW$gT5D$(jbg+Jg z$k^F4=%CV55=~=2B7B|JK46zwld~Vf!P6erCtqJ8Ad~>eyI-T$X_I2be)$pDz(0&H zKg3YEQrZ7Jhym>pWq)#C)pC=Zffab1xalz*S`$kEp&tm{zyb^H8ZX%c6QKvD*FO(Q zlKd$Nh6&u01q{;cupS((blJ7X?v ztP)Ca*ZBE;?G9-!ba^r{UF>a8H+DCoZ?HA|>{$ef?DMF4Oo}$QL#g)pxLMC~tDyZhr)6?JqFr{Ci z?I@apMo>AKsoGZv;X9z!mjB$}6VC5*2wxirYXWd&WYO6s+#N{{?M$w-Qfu!i2aPwz z8MP7L6RAx;fwPEc!zf-xOH-R_zY+^0l9J+CS=*wK2qp2%M(f2u{{q#anaEluJ*4ve z+jw94OFRu94rGHglxB<9B?$*W*F7JWQe>MofPMGE0+Lsx4Fj57ARgAW;f`n7lD6+9>U-@uQ z?TL09=-NY7p6!k{&^dqq_?+ww0Z|2Ygq6NnR#e^GNUzqEV|A|QMq`7stRuW{5Oc*k z1!R&>)l%m2eLPs#fpMEJHdwHqI-lT6#6*{rCS0D`A9LLxAYKD|rbn}vjC;lQzLd=5 z>9Dc-v+>VB{Yfz}=2rtV>{$>7H;BBN(vnN!MTJf?8NnBwQdeUXqZ-1@+W6yr8VEo( zP!L5YqaYoiHHh~J^9v_X^cS>}5Eh=5qL@0$#}NE)A27tkx7>cEa?uQQD&~V?_nIqF zQd+GD+X>Pdc9p*c!jdU>Q%0kPRvvqeqYjJwHc2cTW^Pv9l zES6fJzh+*=+J?JBEFE&bI~@Mc*5TdXt!rIy{KLhWTg_(JO9T4hF@=OrAR*CY^fVI#K( z0Af-t@3{@q9%y|K{~?P6tTHbVjznl5>4fqkw2|{eWE)Pb0FHSS`X|~nxwm>s=YoVaR`>) z5w2k*Pn0l=u#G{sO$&AxM{)l=(}3rp4f?|yE)dMSokhwJbGUpzM={*C_qdN-(`kzu zOwhItq1+T7X!w8h>IlPF7Ih^MqDV8N!vM}@)C^>c6r>AV)z7lDIDmxXs-3cJN0c&H zfc34$s>0ux^;g6eDTvHQ=@hj1;w)E?sTVnTpF-ENJE1Oft7Rx;JEmaedjUZ3I$;i0 zaGU$ga3&Kr3J5x2AXhBdtB7O*1`Kv5AG4eb>xtE5fNAKKm~2A0MBbe;VZe~uDaJXD z6i6yT?gj&cye3kCBCCy+t`UQK?~>E8vj*Ke05zUKr|ItrCP55=?f2MOZ;Y~4xxPfQ z1HFTL{SK@&fk;K3P>@SY%OfU7wKm`V)X}4QoyhpF{h25kk5Gt_bF!LC@RyuEH?q-8 zni+6rbm?X%cNMr~FBWom@FL`FJU$kV6oo@9L9A4MoUKJ33|iLADyN;Ey_uP#BT6e) z2|srOq4!B9xRyP;qNb*%q$RayVYE1PVl9iDtgNiOJSlxfu2DHC(-x9|sGC|WIkKH}2F&0!J@^wVag6q>&+cKRD8AJvA1a)W1?}ZYXgQ+O?DmkS{f?QX zMAwiDq~R1;lliUL(<@73J~kiqb$|Q$A*P1YvNcs!)+ar?3EDLK^RP#s7A27JoVx}& zP}F6WPuk{gFL1E3RaoX(7}Tn%E1VgsBXe%WNQ48E63_Ecc+ca@8qgBOysP6I)R<-~ zrgjkQ%#J!;3Q&$7$6gW42J68r5H`Y)cI+~F!)1q!WzD91UKe4n5If_GSN!R14t(y< z7`vZewDaZ812#q##KPih%FExb`aY_J!w`I4H||L6qlqrBm!~m$4?$^Du2)iQ7LCKnQ%Mb6wi8ETiKp88c>Y_IcgZ&}Lz#)aYU)Cl0Rl{TA(;o-fXI*U_2( z^V{M^G4Fz*9_TA?x0aOaYTZOPk!T~md$Kdy)j(_@1jGUD+%G_JCgTYpHgTMlaU-Pg znUzUODo)DH00T@w<0r-kgMolaKn685Fz=drq|r}oww0aSe6sI-`(0>aKtvBM@_w0W ze*At|_qf=t)M&r%UgA6(WMa)A9Vwat6^BtM2Pzkrb-n(s(OY-wc^s(J?^(;a%iJ@g zW>N;G(P1`nnI;W#I?05hP>Gu~UZe#m6~LksHTFm29AO?8@jBFzW-b`ePiV4VT^P?9 zD4Puvk`wxjP{gY+-!0Nis`tue+iIRO-A@>EY4AvE!Yzbpvr%!mh$BTN)S`n8ok(xV}y`du9O&w`gJ?j|>y0&}O^i`Esga$V8p z3O}dk`1z&Tl9fj9b28tnVF!fBc`p*c(W0tv>9U^X1A1OfVp>PXFvF$|IjE-(`1$w; zeue0}y1KVk_{a(8${%Z?tjIHj&*^3(eiV9C*ZdxH8_$d(nuW@S61|gjo|%>Fb(9j) zrq0fwzv<|O!$UX>gR~1yE7aATO#}|?m0p+79<;C)Ld-0d^`>9aLOnE2-p^2 zfh(`TkX0R<+P@9siT&{qTM6a`l5`KHIsVF=TZFT$kIx^k$VcMl zYb!9k>2#S&V-(khm8B_v$>b?D@@5yIWz?nKRHBd zW@9W%ep{i-#@MH*?-^=TtlSb*N$yW#d0s}%?m@#{-%FKv>UOK+$)d{C#`jOGqIG%_ z*75tKp5Ux_I@0hvQ5Fa~J75h4oyqqL(n2h?-lCEfnX;&L@B z%67)O{rT>fV)N$d_r`7ZN@u%8QB!GkdFdl-sP6e37FUSb~E&!-k{-+v|oynS`cyJT|$4#;zQCwA4 zRn-L>wmZFnq~y@BJ|Zlh9eGJsV-MMh!O=8@R0hf8~-uv(+L487b*Y-){?MEHVjm~dv znN)MxibfCPmj5!^bIPmRYh0;s&+q$IsK+Je+5j{Lu^A@h;`A;*M*Sn~Q|c@6RvV!g zj%|a`(ndAbBbAaEq=xpZjLrc03k+ew*p7JFM0a3AnJ|fFdah!{Qkp21u5wwj;Vcq%%rQg43 z-Qan6NXe1aEb`I@N=epCpC{PAUL?`Ar!A0X*nIr z8QzCK`G?4TgY@NcVs_6|QIV)Zoomra+=GgRTB6X>I4)s% z(QOq%IB{2n>c6PBZUF>Gg!%@Hy^0;H%j5I;Q3D<}qpRUd{;wZH&Lhy#0dY{`Ub;}2 z7Q6wri3LwFVAc#eN{^WD;xC1C124y1GCYhY0OgZ`ePl4EW%> zaLeB(o6C0gO8(o^S^Xw;H0FdQle=qDX+yKLdo;- z>dqHt2hC}vi2<%K{Upfu&d?|ju(V_%zha34Ml5LZ+>Ai1_d+rd9Aygw5KA;uIdl$9 z6bY~wX=bI+uSN_$-z)KB4d43A=fWrR0=oxC0;jy3hT7)~sK+nAbL>(YJes7f%J9;UlS(epDh|sV^NCbvz&Vx_Di8Nc6t0usE@?VM}ctQiY20ciVBd~Boy{4_7Ujl z73G1D^~+6X(}rmtr`H<_P&0{=?ZnOng_*2^fXHYWM1A0yf0J~AR;92lZ<3Ot+maxH zTV#Xa$2#GH;7KX(8ml0D60%8ENm5{J6NNmIuCDkGGGgQ%WD1Xx)nFw`lGH90iitYy zbi*FbNeusFEr5~LJC7rh2}kza$o=mpEy{Ib{XeX`=}$Mk{?*J?@2&G~H+ePxjjKC4 z&+C!`NM{-k^-$x5WlkNMY2@HYuclsO=e-~5Z@@e>5%@j4F09^`|EH4)y5+HGkufbf zXGGbUwmhD_y1CNnF!TG8xtdkC5r$N$Sed#0G+u5d8Q=U--_|>t-sD}!XA;W}B@zc@ zH^W9Hi3n!K)JaDS6(L~knt&l-He?~-oBf}!|cBa8P z*6X(TD?3w}Qk4?Zn>@Gr?S9G0Uv*WrTFn*+7zO>1?isVVG5Rtf zD{!c5*dnl;RuFF7d#*ubhL7|2}VkIT z*=n|UiaIB3U4?nT;0k@t5^C0ae8cH4)b0NK)pt$CZLIdBtF;KRu+;*8nj`}*g*t?F zKUg#&nki%&*)|2(n@1kVu8kCO;JNK}93l33S-IQ-};RH$;^fg(y=mJC;S@{ZBAlYCt%Gtl!1IV16-4 z{-?UK^I^fxCko7Jf-Jwb^k~fQw`&iYTZ*v+fo;49h<_s>{^_2w*Zz+8_fwo-h2%Yd zuF;&GgMilZSURkyHl4| zY%DQN)JUTX_7ot}PNx~K=usV^$AgH^|MaGH@I#eWJ$o)QFL1^ zsG_&No49V;3eff4%b@@JwE>@}>$BD*|Lf`Zq!hqz7HS5t?;bAlsS`b+?zWIIL)@wVLu~qruhXu-D(AE#g5=Ep11q? z5O5$(ML*S$X8(I}p99i|_$5^*av9jB997a?ZS!2I+QHS4T&N}>FD@>I*zMEL#flbt z$fAP=vCItw(=S+5&|M6cEToJi8QCZWf{(;D5^!79r3ZeNoBiJ5qc%Dgc31Q7`XoKByiWJOp(MXiJcLZqqikLyO^CK;GlLZE z;-*>)jdq)b|9J0KzPWV9^Se0DxlRaf<6D234aw$prgVKF)aO$Duc#6Q2c%hGn= zy?6WM((@(eBVxa-Fr>7bc3`3b!G@$%TlB<0nx@#sWbuGJ==p1mEcy&ZjhW;DfUx>=L0zsO}{X-Ro@MNh!mZij~gs(R2#Tps@Sn#fD z30P|u5-#e?~>oJZ zbw$bD9{dykE9@TSXMyFLMbycWg`MGH;|h4+vP~NG5yINKLB^ZE-LHoFRlo1bUD`3X z3(-_=a>!VARUrDJJP20dR!I5k8bkx`g1!(bLnQ3r$PIv*c-m5z4rnKzao?hzTG!9X zyIW63{VU6Ccwn@s89X@A;V;?%Via*>JhiOKn~<0#8celinK|0l%xdKON&F)wXhswp zUf~3LbNRLVpBLpJ-W_C2CBJeBRgZR>mnu?XERrB10kvsItl3vWG-6w5XT^6!9C?EO z;DofF$vJfJN(17!O#N@!6_}LrDgQ(d6s3xC4n5>YMCx zMZ@%W5v)4r?m=2lS^6g#$JkdTE#efKl?2sOrV33n_?BktcY0HaD3MGFc1|nmlW#{k zh#RjG^!-5p>;vjZ*72do=hVyV!O-oCM=4s`vJ|ZwcDTC^1_AbRXqA|fFf8=u=a*~; zS3N9zJSnjGh%iF1JGHr--;T|gy0#n*6_t#HzurHCSxbqhb=#i$rE6b|UV41EmqQ>B zZss7cU;`zXa#L`+VhUrAe)=k(WRRJTw#qYw69X3gg;jijL& z)St2BxK*jEKgTKNnIDWcm3G;7wI9lEj~@@jpGjwE8S-Wb9CscFO^#3V0 z4u$~}LhqbqSg;V;it>4DCvaZ8k&U26%n4sq@a;6q7Pyb#ovW_*Vpjc}lCw>sYSVoa zL6r!r?%0zCRO>{-6Mlj|*y_)S70U%Dagkq_pT`*Gk|{&flJp%ZHj|9`Y=R%mimGymw#KnnY| zm;qxYTJy{~TtF|Ox>dzk&qs5dUHa{sqh`;FkPmVVR!S0zegw9}j;-w-;PP^NSCG+q zWpYvO8I8?BnT;=qEMr1DnI8$Pvz$92j&Z7>P;NG+t&m&8MA=s8bk~-34{fzlS5?yZ zxo#YK-||D}I1;!%ivd^w#;pa#uw$9_G#QGzW+-5VR@gEDtkyt%Ar8qd&=)2Ur(jf& zM0==`5;T-n*m58F$iYq95r)(Zwih>2AI5E|bozX52Ndct8+JU`yj`2zLrNHTSU3DT zOEb;^r#?j3_%9#YJ{lTXHH9OC_x1q1F;i?DzU+raVkA4S`T9bm7d>44&}5c)8sisX zj-QpMMeMnrx9Q#mSYHa|Qwi|WX5|4?mw#lDlt$rcl%>$`a%o^4D%Vn!k2A+>^6Qmi`&}KOd0@}voJOFzh9|?v-3qW< zXszT&afv6!2;Ym)$S%g<*BJaH`Kcw{llV$W>t2XP9`ZK0eTgjuqI*zY&a97% z*^m26<^nJ$srw4gYh~1lLgz`%6o_bCNt0uRsd(37v4>ygBOkQ!7TeEn&OjDU2c*$O zN4AWl&C_XeoqON5=N_5iq34a%@K9gVcZq^`4fm-klKxKv4CpiN*w)05U*Oy*Z6j;y6B+|g+PB@ke z1^kg^FHVzHwsl+n`ywBM(acVqxMQ`LAKI6ZR}i0}$S_>=1yGg4@ko%yI`$|r7-D#5 zHl!3ipzF~7XcG{u6bisU!~JqJIE95Yl@?)Q$_>}?1J;oRoXCp~ljx=ZR~%o#kz72m$BuEHG-CB*TF1P=%P6z=LR&unj|b9~U(<>sCEb z9fQxYy4+PI5Hl~SpvqTf*CKfbGCej?W zrpy?YI!a0G&n&JbGQ`VTQSUIwm^(5GqmGenGwBYm@Z)0wE(fRbNo)*&OU`GN+ANi` zY?k@k=q$U*XeVXol0sf`qUZHrGhApgvD)z}?y2+Xm4yh-UwNaWVoA|RJ)f;KT)gHCZM6;7U3~1O zmNQ$Xk{Nm}vKldW8Cl$0Aw1o*NWcc$H>M34xyV;i>(EVEvY8e!IQoZ8F;GPQwlR?- z=LH{enN@G)#Bo^!NkeUt^TT@x3ahabZ6P-&r`!oIV#5bMLC%6T9IE~gK0k|x2iiBkgK4=F7*uUUD)wyC=N!WMfpNcXAd-li4krzXfAile1gdnS+}uFo&Cm& z{!Y6Zrhzu8!lzdzZ%t_#{e+6q60EcF-_+X^!H66~jLBtHCf_#?`Ao9b?z<-!ulynx zwLHBJ{d%^*~XvHsLGH3F@h48x!0o= zU;60}|2?07|B3e14fsB;aBYWki2{^t_)SzDVg>fZGextiZoUeJx;m~nt3n4N-bJAv zSyFyW8c#f%z{ymv$ZD zvq#sCTpsf(D$e5=3H^YcIZ61}a{Aw>jP^+u0GeZr=Z<@&mS$6ba-NGrJ**{APtuaC zRJ>0T2?V62pmzbp^zo4aKfwyK_a{2iG=F$d5vX?ahkZ#KLlV@k8)V znL6Ta>&M$m3qI!g81M{>DjnKve*4a<)aMae1iVyA)88LS7Uwd-iGX z?OWN{*phHFY}Z@(y{EwhhSu>*#e#lp_v*Q(8g1Xxaa&8-dY=ih4FMfAIft@{`7Q_XIQ!yB@ zyYVm>^?2-dd6xGHJkN<)?av7iHG<#0dvpG{ap%lttgK9_S93a@PG^638c{*c%qJvF zrGe7vHJ@K0@^@dgto43y=+o!v`F=cI93CG2vHjh?FQw@GT{n(<_whBVtg7PX=C;1N z?0qU>3@FuZwJG41AlXUwy6Z5X&E{${n*kZ30^9a^v(^XwESQUpjdcdt`1RdCi_a?+ zRP^-pymxfyci;66g&|f!+rtGE@OorqS@2}mAo4vSQT1A_(8vVfBaD5&?#Ul^-Tm;8 z6}%XIzHHrgUe$hozA6)KV(?#d;3IZr6G{{Wo^lUYwM%0!iO+^W zzPRO0!e8=nnAdP+Ae;G8?nfdsKt`vP)z96*LVRHn=cjlXuRP#LPh{oHjP>mBRshwS zB=iqbGY=?m3-P`T{|lY=rNzbdDF7+qV{v6=gqBu#r<&Uy%8x8bdVF%W zwxTA)CY3=uF)L|^QlYiei~>=nuz0WA^fB}qw>?La9&CBL$BX0TB7fo75hsmq*ZRr(**k7rad3>)b1EHDM|6InyX3Eu$xa4V}3Q%U=h09_T_q`8k!+3YTPC)C^8?C z4Sww7YOSvCuJ_Zn=UJ^h5Ug0I&6Xb3+t|1$*-S{3@vJnrF5kLE{E2MxjF_q_I&j+*8|4FHE!IgJqox($K&1pP4QRXqR1t%S3GN|g!r<-c`48su@#z5&te>~@j>Qs^ z3c9&*kRP9&9HgC$Clpnm$$-h|5phqW$o}QpD`X&LMF~{wf{QsmK3-f73PbqlK8p3A z@U*Y-8%T^}Qy_$F&Y(Wp9IU+aJ0c|fw2LEX>GW399fa9W4iVtm`0wJ0NfP<{$i4kC zGD`R*_;&k( zD8Ix8Ly>xcyNeJeHJ)3rQk&)#f#(yy-_KpIBj0UqS(WWPI>Aay&BENY1=;878cdJS!lm7l|q!g_!4Qdlk?3N7>qT^u9gE7JZb7?-Q3O{{I#dWTX zo?+^s9Is?>YXZQ|naK9SlqeW9A7x(mFvl;g)^!@W@V=v{T` z1j>B3*O|-MhB*SF__QU$P1A_MrE!!OflX^j#YiI?S=-6^aJLpy*v6d<)9&10mLpS3 z=weXb?`s7Ni+wt=!b~3JXU9x|7bnjzLAEw81IEo7@~Pfe%+YAXkRP5-u~wLxDmLIo z8(e5HaU2qoK0%*h5%Q1ca=3c*9}GijpB!}_0Q#a11hORS;-$FCpJ>Iz3hqyjGegSH ziaPhyVB(Ln4`Ln=u)pj1A5N}}%j=D1=b?q3G#=(@bY``IPb^j-M6wS~(T3`V;w}yz z14gaQquVKG(KgX4dx0;-sVJU7X9TMej{FOC5>AnX{5L` zF~Fbb>mH=!NZ?$$v3nDYcdeh) zl!ONuVPJKSocmWdr^IS`HL}OG;VD8u?V#-0;&vzL?rP9M>o2RXjz6uq=<$*>;v$UB>4zV;&ydXv{X`aa?xXtHT-bTG13&69%6t(i<3*KrtxE z;{?`2tZLSO)K+iN@A62(K9*;CJDowH+Ny1>N`&21?(1vHYwr1WGpB4u ziv6~J-+Stxw%;W?LO;X(X#>DIp_8r@TM``_#7ZG=O^Vqk zKJMMLb7|=I5T9;5IZ(=f-oZ1ujqT0@jy*mG<_;NuS)l%w z!5@^pye4gc(YlAhIDu%7y@Y$!-@R3ZHi1iCvG(}o5)O(tJm+qyk2blNFLMD8fWVa|SPMQBM;Qz1eV=D~^#Wj~(YNKW~D>L;k@r_~Jj zzRg|U&_~2`LL{U=^ZKnI`tGgxXW7jpyHq2Hx!So3;9%|eue;{G4+e-t+cNV2URIi%Wz+Q9+JNj;l z-EnKuk1CzL^f_A=8e7)cjvIfWe?#1NBJ!5AV$Gaq3sru8Z-)l)hB>&}tEaD3G}g}S zp~o?e5)c$YJ!8tDQ#qGgL^}+g!-C#Hbp_hysmte-k*MdjLKGj`-)9M;pdBI3jmHA; zpL@j)rWjR%H>_H<_Ay12DO!~tW4vOtekjDe|gRhftKLILf hJ@A`{G=KF&Sbql;B+YJj@V{%=lA>}VwL%7g{|7vV#?Jr% literal 0 HcmV?d00001 diff --git a/blueprints/engram/template.toml b/blueprints/engram/template.toml new file mode 100644 index 00000000..2b09a921 --- /dev/null +++ b/blueprints/engram/template.toml @@ -0,0 +1,22 @@ +[variables] +main_domain = "${domain}" +pg_password = "${password:32}" +jwt_secret = "${password:64}" +cloud_token = "${password:48}" +admin_token = "${password:48}" + +[config] +env = [ + "POSTGRES_PASSWORD=${pg_password}", + "ENGRAM_DATABASE_URL=postgres://engram:${pg_password}@postgres:5432/engram_cloud?sslmode=disable", + "ENGRAM_JWT_SECRET=${jwt_secret}", + "ENGRAM_CLOUD_TOKEN=${cloud_token}", + "ENGRAM_CLOUD_ADMIN=${admin_token}", + "ENGRAM_CLOUD_ALLOWED_PROJECTS=*", +] +mounts = [] + +[[config.domains]] +serviceName = "engram" +port = 8080 +host = "${main_domain}" diff --git a/meta.json b/meta.json index 8be7825d..0c9e81a7 100644 --- a/meta.json +++ b/meta.json @@ -2155,6 +2155,23 @@ "mqtt" ] }, + { + "id": "engram", + "name": "Engram", + "version": "1.16.3", + "description": "Persistent memory for AI coding agents — self-hosted Engram Cloud sync server with a web dashboard, backed by PostgreSQL.", + "logo": "engram.png", + "links": { + "github": "https://github.com/Gentleman-Programming/engram", + "website": "", + "docs": "https://github.com/Gentleman-Programming/engram/blob/main/docs/engram-cloud/README.md" + }, + "tags": [ + "ai", + "memory", + "developer-tools" + ] + }, { "id": "enshrouded", "name": "Enshrouded", From 01f532d02c84aba84ac81022a6d7c37639261afd Mon Sep 17 00:00:00 2001 From: Daniel Esteves Date: Fri, 19 Jun 2026 19:17:19 -0400 Subject: [PATCH 2/3] fix(engram): use static internal Postgres password in DSN The template helper's ${password:N} can emit URL-reserved characters (!@#$%^&*), which corrupt ENGRAM_DATABASE_URL while Postgres receives the raw password, causing intermittent connection failures on first deploy. Postgres is not exposed (internal network only), so a static internal password is safe and matches the convention used by the umami and documenso templates. Real secrets (JWT, cloud token, admin token) remain randomly generated. --- blueprints/engram/docker-compose.yml | 4 ++-- blueprints/engram/template.toml | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/blueprints/engram/docker-compose.yml b/blueprints/engram/docker-compose.yml index 6e72b17a..21c2fcfb 100644 --- a/blueprints/engram/docker-compose.yml +++ b/blueprints/engram/docker-compose.yml @@ -5,7 +5,7 @@ services: restart: unless-stopped environment: POSTGRES_USER: engram - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_PASSWORD: engram POSTGRES_DB: engram_cloud volumes: - engram-postgres:/var/lib/postgresql/data @@ -22,7 +22,7 @@ services: postgres: condition: service_healthy environment: - ENGRAM_DATABASE_URL: ${ENGRAM_DATABASE_URL} + ENGRAM_DATABASE_URL: postgres://engram:engram@postgres:5432/engram_cloud?sslmode=disable ENGRAM_JWT_SECRET: ${ENGRAM_JWT_SECRET} ENGRAM_CLOUD_TOKEN: ${ENGRAM_CLOUD_TOKEN} ENGRAM_CLOUD_ADMIN: ${ENGRAM_CLOUD_ADMIN} diff --git a/blueprints/engram/template.toml b/blueprints/engram/template.toml index 2b09a921..e12b5157 100644 --- a/blueprints/engram/template.toml +++ b/blueprints/engram/template.toml @@ -1,14 +1,11 @@ [variables] main_domain = "${domain}" -pg_password = "${password:32}" jwt_secret = "${password:64}" cloud_token = "${password:48}" admin_token = "${password:48}" [config] env = [ - "POSTGRES_PASSWORD=${pg_password}", - "ENGRAM_DATABASE_URL=postgres://engram:${pg_password}@postgres:5432/engram_cloud?sslmode=disable", "ENGRAM_JWT_SECRET=${jwt_secret}", "ENGRAM_CLOUD_TOKEN=${cloud_token}", "ENGRAM_CLOUD_ADMIN=${admin_token}", From 16eacb1fdf8a9175b7928c5b7ab13aab7097dbd0 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Wed, 8 Jul 2026 01:11:26 -0600 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20point=20domain=20at=20/dashboard=20?= =?UTF-8?q?=E2=80=94=20engram=20cloud=20serves=20no=20route=20at=20/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit engram `cloud serve` only registers /health, /sync/* and /dashboard/*; Go's ServeMux answers "404 page not found" at the root path, which is indistinguishable from a Traefik router miss. The deploy itself was fine (both containers healthy), but the default domain pointed at `/` and kept returning 404. Adding `path = "/dashboard"` routes the app's real browser surface (login page / dashboard). Co-Authored-By: Claude Fable 5 --- blueprints/engram/template.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/blueprints/engram/template.toml b/blueprints/engram/template.toml index e12b5157..09c58b40 100644 --- a/blueprints/engram/template.toml +++ b/blueprints/engram/template.toml @@ -17,3 +17,4 @@ mounts = [] serviceName = "engram" port = 8080 host = "${main_domain}" +path = "/dashboard"