mirror of
git://f0xx.org/android_cast
synced 2026-07-29 08:57:44 +03:00
sync new docs mostly
This commit is contained in:
@@ -157,7 +157,7 @@ endobj
|
||||
endobj
|
||||
32 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 45 0 R /Fit ] /Rect [ 45.68504 419.2874 129.269 430.2874 ] /Subtype /Link /Type /Annot
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 46 0 R /Fit ] /Rect [ 45.68504 419.2874 129.269 430.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
33 0 obj
|
||||
@@ -275,7 +275,7 @@ endobj
|
||||
endobj
|
||||
48 0 obj
|
||||
<<
|
||||
/Author (Android Cast project) /CreationDate (D:20260611185331+02'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260611185331+02'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||
/Author (Android Cast project) /CreationDate (D:20260612190939+02'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260612190939+02'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||
/Subject (\(unspecified\)) /Title (URL shortener service \204 specification) /Trapped /False
|
||||
>>
|
||||
endobj
|
||||
@@ -429,7 +429,7 @@ endobj
|
||||
endobj
|
||||
78 0 obj
|
||||
<<
|
||||
/Dest [ 45 0 R /Fit ] /Next 79 0 R /Parent 49 0 R /Prev 77 0 R /Title (18. Routing \(normative\))
|
||||
/Dest [ 46 0 R /Fit ] /Next 79 0 R /Parent 49 0 R /Prev 77 0 R /Title (18. Routing \(normative\))
|
||||
>>
|
||||
endobj
|
||||
79 0 obj
|
||||
@@ -456,10 +456,10 @@ Gatn'?#SIU'Sc)J/'_]eX;jIs\N=_LLB/:P7d=7ZK9d@4ZQuObjX1#A8%+ni6(61%+kQuM9$b[Jnsl%W
|
||||
endobj
|
||||
83 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 3526
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 3529
|
||||
>>
|
||||
stream
|
||||
Gau0F=``=W&q801ka/DL-8rj?E,$JbfTTjs)*=#D9iWWuKHqlY_#[+L]=%Ze)a#C'i[JM58JDM>GGEPNIpW9baS8W<J))-\LBUA$=$TVkYm*X`FBI>7mGcCJDj6uoDFgXAa?!G3/K("Pj*TF;0.M<OWarnS#Dt(Hmnf_c[Laj"+23<J%<Ku=%229=,'rnt(-aRf=g&.U+)X;T^FamJb.lo5aT0MMjDtctZ]r'94AOJLM11MrnMG$=VlHi>ZDf80[I5OT^;jHt',jb$--s[5)boW5:85#.$%^PqT0>+OGr+cjE9@Zad'-j8`.TCMHt(b2Mj$dH6sB`98P#He5Yk8ffh]f#mig[Sj*pP#47WICemBfoj<n1,E!n:TLl5!VJ<;#;lfS$BS^U(YbW$KU=6fVGDLcoT4PtDpkpl1.fV5]I$n.]E6h\/eOsn&5:1Iui5<hf"D[+jk)WuHZ7O&+75[5cSBqKZ$ir5aI2oY!mgdb`MS`ODB-fpgerbH5aA>pL0nI,5k\h]M%SgaOk-)&AImuf[+*c^n+\k/e;bI7R9V\lU>&h[=j=l>kGXO0U<=K85u%^?R'\W:EKrUAo:"N0+gj@_.:(CV19.]K^kM.&5bO<$p!A>Q&R=BpU;d6WsI=ggCQaHCD/m4(fYhKPpo>#BsR<OHqUGcl?rOl.DE&W:A<02,Ye<MLS\W,f1=3ICB]>+_U,%PN?e0(Z"HJ'ICj?m0c:LETfm\M`CYY]'[Ni>\6(&hLI*@`fd@9L=1:Ub7*hKBRCh#rsQiMS1j(n?(%N6,?.0G`40>3a3"6I)P-.D5JkC=[4W4#>JH^5)<M47nJ(/lSM0nKcOCn!EufUpok7\;nRL9EN*+Q1>KGa76<0S\1=?Q\Al229uKU$":+i=8cu`0Bp,q!Id,P*Q*khOIf4ci2quLUGNp<Wm:q/4<NSr<O1P12K2Hb#DVtn=ZCI$G"[4RT&&R/kiQ/^L2k9l<qODlq0C`BfUJ$KP/Y'f2ke1fd0fk'a$!44Z*2P#%cUjM(Gt\sMP8'c;9Q4K8BUgI%P7T8<93>&]GRb%trTSFUc=8Eq=\+/&j]%+f.*[<8ERmt"#E-RWo'MP/bKBVoV]Dt?7hYT'2Y_s'0eQFle*IYUitUN;'T:6_mM68UjpV;Ur-=F,.8JoZKjN`J5N_]i%^=f/Pk/N`Qf&VoMkq2%qg:U2R/SHCd`m8GEq>Y,Rmf;k0nqH<6c&h5#Q<_%I%56i],1`J>Hp]V9]hoe1nR7W&/:m^pGWM%hdPU6O5W9Hd8@#HR"?<NB\cW;3[EBZ2+%I;F%k8m6*^pLd1m7*+hm(EiuCWj<"gl=1PcM12\[])72L?NTo5.le"QbH]OVqoEXh[M\XLtt(L+o&`I;*51R>!%Of>9,'N"s:mTdZk:7hi3Z69&X@U?qI`)(e[3`)9FWKJ^"kGYmd'<rj-W2[oPl_JrZVDUWN;Upe4.J*mX""Lb.90_3M<QL:VQhXu?:km.b]Y3B^^U^"2&"g2`1W1Hs9tJUu)TR8p`"Z5Q6\NTd'kq(>@l;JJk6jO'$h'_g[4'$F\:=O?`[gtCHTg,WMB,!<66u+?K'!PS)!Ti_Va66.Q$3f!rqMH0?O?JKbm0%<K_YLu)R&QRo#./fQq6^u)*J076Y+.ADs,.ZW-2)\'#L1p"^*^Q9DOU(*/G2XGpl>E1"js#-<a/b^i0;[-AM_9C!,oGh[(g-]^>f!\Gmg..u?<>ZeXMpCK^EoH5ZK>?XOS1p*8OI:r"I;"p(ZDE-&Q%0P8C&`uFP1o`T_6$[-6GppflR#dq5jG-$F:1Ct6(*rc)%c)JLc3T.`t5%+*(lMI%!M>>E2d"*(A?m,TlQ<jG"r;5N6A?2#6-rBr8I<pMgjf-JK]0fW9VZ>j^K3\@\55JDGoW.Ut(5%IWdXY,)""*J/"$VtZR'J4Opf'.Z>-9^?;qZ[sPm7.b2IMQI1uM<hTW1aN$c;-+6XfVr2VaS>0su1.Y&!aF6Ot2T-u^;<:2ti$]'=aF(M(BuJg$+)rB@:REQU8\3r!JV9gs>'&7;5Tg"IZZc>-()R;gqT+f*VG/+>[@4_KBT']TK9-`(c<gN2,GW`urPipR#o+,!F4c&d%tBs1:qZF93U&t^?gRH<d3Uq4(n(sR*TFP-hGYY/Pfpu,>E!ad\ToqGk0BD/!p#7W1q,Bc2$Q\nXoMWg+Ppp(uT.uJb0A>e<q]+NilaL1(U@ufX8k:d"?T,FSBVs7SBXr'O3dP2ZT)3X;TMlj(:Z]V_kO3U9PA\_t9X`]o<+f5UB0<kA$h";_&^nU13CL8O4JQ#5e,#\AggU)iA#tMo`n7l"0'DR'70^gAPll$P5!+!rA*Q!Mj?(qgJ8L\gBL>:hf'&t4>a_E4]=7"$oh&"7+%;h]d]ITP$EAeT;%Ub$DO@RcoRF^gGV_NDh7>q'7WpU0Z-#DhV2RJBg[u>+pFXi2<XkS34T?f5Ed?,IZ?!l*s&<Iq'k2bip`Uq(GApPJSE`f5$mEkoXV@VVU9bj==Z;T1),EclH`goU7_%W`I*C>aY]n3&221DS\qF=a^D&o`^+)M&;lU0<\E=l':`H?h&IeblLD8hV.6]!EZmu/<Z3InIqm2AJfj(*Oal?P-Vi\X)ChQ*0((8^;?q,>A?fD_>%oMG@b(jo<VXSeXrfIm8&8;4^naf11P2(run\5"!XX'#>'/#<`:N5aeZk-HR/kUp>7n<X23e)q"#meci18Wr#(Eo#[Z$Cm5:&PcL->SP:90&.1;lM14;4b/'CQ7<78jakW4YN<f(*/4bt+c^[J<A;&6`onVkbSeX$7jO(^\>"Mn#E8UTUQ_8:dk/G5g,X[`3sQV.)JW_==fmTT(rQa@(^KlQ!.#5eU\::^S-7?jQL:dkP<9JI.k/@D!J6V:ag,Uj.g6nR2V/#1Jcr3T2Ao\PW[l+prD5flPbm*0*/Y$IOj20;PC97J53T83N3a"hd>sOU#9DMqTgCgT.b(4[KrT;s(J6WnQ5<NRQ5!83^D<KpqS'ib<*"_KQDHnEp03\_-Wd&Q,."`.J$buCF0sEBA7&+@RgJ7n:MDBOs1o)`9DoU4d*tN,a:)I1AeQW["K(cI&k=m'BT=&).Fl6;`Es1`M>C;E(>R4T?H<c6ML"3X!p'Fah2(l4-)Lbi)r[:fr,B<u)7!Vj.dm,[bfS+^?Nmn#"";rLWRn-4-^]e124dg0T9/-c$DtaaBcKp2@(lJ9J2A@`SiY0CgS4>ogL1H8r!rKdJW_)E!?QXjgN6GV`qc;.>7X>m+Bj5\?QRqifEXf0h!\B7j*QaQWP:OlE;_N+BU#p3Dr>n\=-\9sZ,h:m;sPZC-[u"E_uNB7HU(4im;\]2"**SjE^aGgTm+Asq#r<H^8U"gib$d6<U^8->np1<_na[6?SaU?LqtWF`?kB>SE'qlRE@#[bGaH__R<LXi[BuWB^PO:D+B`u7lK,*WE=oc,EDkI$*LhNob6VJ]64;%,`IAYIlq\](*#8!rq/N9BaJ^S]G%,oRDUJc4?(QhZ&&b!&Cn`ACph%OGC$@khY[n.8Q.h~>endstream
|
||||
Gau0F=``=W&q801ka/EW-T8s@E,$JbfTTjs)*=#D9iWg%KHqlY_#[+L]=%Ze)a#C'i[JM58JDM>GGEPNIpW9baS8W<J))]la#QhB=$TVk^&g`iFI:t%mJPetn!'QEDFgXAa?!G3/K("Pj*TF;0.K%dWarnm$]6LLmnf_c[Laj"+23K9)s4#%)CCQY7eGsck!(W7=g'WK4ktM/I[g?aP^g_LP6$t$aep%2@juUNGatju,Q/[)j%7f9:K1JZAQ)GI[E"L:O7@tpKSG^aa\+D)7al[9PA'<d6f@UYVpEUWhFp!^gj0&21orVq0d0+,+'J<QioH7*d>#Hr/>Op*TI21Dm&.hu#Dfn9\=^FNQFoPsnQ-#ZqoK%+hL\BEiaU@^?t>)h\p:l:BCq#f1QD\/NI$&b*M?[o:NtY_\dWs&2X%4e",^a@O?INF,`8cPP?;#10/!sulN+Gn`p'(_dYL1'&=88Cg1T9*\3F]k9ecDrGL9KA-Vg"4k+r*tQrs5W.Ds6]93][WWaQs_\ne=H74IGadN]D<HCXf&9B,"eip6nmP>7#Ok6%6Q(iNLs/m5;Yi#TA3d9dPhW2<RTYn2)%+49TSiTlU3L;/qnQX7cl(buM";J"Ys_eAH\4-F3t8k0,kc4%C]K-ja,Cj0u_-/192<LBYslVR2\U'Oi$C?]F%>kPHJ_*c?i(=R_>R(CkK7Q[,;j_2bVp>gS-5"XEI?2tF?k"uZgRbl$!6La1SA$S:>/&i`?i>N?Gf//=1Qu=CkPX"u2O8Khc?2TE]dIDMqE`fUZgM.G]>G3ErJs1lX(milt^)GoC/r+7MARWdaH;Q`ebf/duf]@RH`##XMPE]"uZDEU41u8dk=9\%O?CYM1WYU6rGB$1$cH?r?ctGYH0e[Y_0iC6P!C)@f!:O]lV!Yg%HF@g)m2W@b!5A6oc"2EpG*(C#2*d_4<[PFK?%'K(W4E0ln-)KgS<<:U^<GaN_VH<l^qITDSGo)US9jBPnR!3098^,/SAeOm]klCW3emD9=00smV+2KN;4+A/9GOFg'VcpII$Z*8cL^C"L_]e5/1F=VLP4Xc<"CtI!YJq!cU&6IYK'ND>%nIGMR/'&4daRa(?\')0.WqrS9DE>Sk.ddg#*O8`jeiN::!GIN)P]4:b4Yo\/R7YRkk?JnE36KVDsP[(4(\prg;OA3P]sY;,3`H?%Q9[/='J0mIU9i@.KpL8D!lY8^h0IC_ktH`Yci8%LE5r*V>Q1p2SAHls^eoDo<]J1RWT5d!Q`L=\t(.h;9p1?&+QS5DK'Z77Xah@1&TVU30D6k`B=ZeI2m4bqWeQ#0AW"7or*GL%q)]N,p7E;,+R=bTJg^hb^-D'OC4)L+Z@N:5A[inWIVYau.Z^jD%Gn>\k92(]/?sbm:)58#E"%Ysf+<\T84H4+/Hkab'"tLj9<i'1;.Yks/s_VFm_%Stb4.9uN@QTr';4XHW>YRaqK+8l[t$VU-QT%B.oS0E#k!<?YYb>iLOD5T)1%n`GuZ'^('24ab@lcObkj2sEA5CE3(_&lVm:$kXt-<MY2>O4>qtS]q2//A-`>e=me`-j0)PBtV;Y\C\&=Wn7r+-q#8_+_^:?L6:>`E#Q*>=a:N$mZ0$rrJbcggjFc76&=D_W:B.:+_CS.M2;A=R*fF50LV4cn91h5JSI.*+X,Vi;ik"s\Wh#haFP0?R"qs]*g81XB;3@J%N$eu_;p]n/57!Gi&b@9Pa=XC!r&;jAgL;eE0`T`E%Q-\9.pBslO/XqdXEH(&.81Xg`QT>%W3oN!lbWdl?FP&Vk1]mBYcS9&g(3S)PL3``/`C;I0tS#^r)e:ZT/bf2?u@3fa#aX=FK(I3\e8B(HO7&(+_l3fWqRXJU`0^PAJdt%0c%Se'IIoUcb!SH+VBpp_:LpCQPU3b6L8TqgOeOc!iB3<uUS-Cr#+;Bu$XP!E40<!M@L=9Zr\*Hn$*)WR?<Hap-(pPm2WY)cYA0B0dBJHROjPhM/Wt3MtA&bB;(dFm,O:%0[50bUG0+F]Slgq2.jj751O"V;Go35>E*:=9j=%W2Z`f$0$l-/mEZJb^"%s0GBJ9.>3q3E<VrS\EBr5JRUb#ihR9+VE8\-:j)(^:Ah5Eq1J19Xr3D*3pkH(?.O0>7cI@B,L`/'MGtDd)l$P2RkD=G%sFqr&O.7M;rAUl^0WXtilphuoUoX3CrD>=n5;!@:_EX\&7>,5D+1^*OR2V/Cc?t#*k@TlAB]U0r=ik:oeQ\Mm8VmL`YDuS<]WY/2a_WABX*g;EAI)co[+OjWXK`(VR<St2T/RGoECQ\J@jYO%9Si8F6tG'3H0ji6q"'4D4=R'/)c:d"^]GGFoq\>&E`qeqd&4j.6n^A;29+a6^O_R>Z-G"NZiUk^Voo'CU+jNbnW;q;"qrgXG/WoC>P6$^,RHt=9gOt6-,g8d8]H+NRO7VqkD(.GuaTlTG"^*;B+=Y]j&K6qDo?ZUt4o8gs9RLQfpu$L9<1E?jOjIH1UCWfD4Lflt18YM/l^r471DqGH)23nkBjlDR*0i/gHNqY/VJV=gb$(NmV%j[i2#k[&):'dAlle((e2DKXDX5&Z&W82[Jjgk;d6,L7B>XFRaWho^L'*UqLKo3*7(hj4dGWUIc)r.>%.tj]&T8<PQkP6C,=YmsLr.b%J]OK<f.OpIrA?$&Cntj:tEb?%f2&OgDrJgA"sml??M_Fa.H@kBAeU>sUfd&`\PZ(6Fru7$r'\e5o5)"TO_P%c:4CN3nF\b3J/a8=jh[Y'<jbf8&\n"E_"M;6V\aA0uRR5GmEo9TTU$X<XZqfpt#j(MTbl?nOSdBhZK_"Z/DNlt74Wm<"t=&MDVZ!Nb!8^l[)C'<B5i?!P5h:)M#2a:AL%M\mD@pN5LFTUn)a=3W_J,I+eB1;"/@BM"I?S*$qQ^qO<"/YFRad$e^5TiJ&&Elcf*,m6M:W_LXMLu99*m-HU_#E%l]*p^U$CHia95`*5,TcFgs[!eW74Xa:.C1h[_F@0hh6'Z:Y0pVHJ!K+#J3h/HH<L@W*)ROFt-(/mr2<l;>CKpa-48LBr48hkHmDQ5sE5)"0`\RQ9]PtO5?U5&oD=iD'!*3bkOhsk$3_L];FAmtW\5=>H*DB<-'g\=fJ(88LrN+t3/?pdp-XJ9!*[&ghR,:nQg-`.Xc5FD.a%MsIqh;-2Sfc2sh)tI4?u%ZN_Msn.SU,a:WE<Ec=U+[.8UddL6<oksnH90LlRrE4ZN9Elj8?PA9!>o[VXVguJH$92km8HiaX=;aQh>$^m0g/kTQCL7jnJT4U"taB7g-bYbUONl0tO:^me#Q4Vu38df$_9`kJ^_,rftMJZN3b%O/O)poreo/,g#HT/HiK%Fo!/tV5qccM$8Xj$8:QYYXJ;]L7a?m=d(e\,><2Z5[b*(8%X5pfVftuZXaf;kkfFa"'f@dDZD>`N^F0.>4IKF8@G;$hh/9V8N%RACqliVm5p7h#1+[+k3g5bW%7;i%60P*p:C7n_!+I1g][H)"6$%%]Tr%_PR_:8VsO!035<H[,4Iou(Or[Nmb6Rg[/tUI5?<J>P5~>endstream
|
||||
endobj
|
||||
84 0 obj
|
||||
<<
|
||||
@@ -484,17 +484,17 @@ Gb"/)CNH71(B'h3EBh$I3Z.C1VKT#g8NQ7l`(<&9c>JoJ;T-X7'+>"_\ij9phtM^3#0]FYajeg@=1T'_
|
||||
endobj
|
||||
87 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2854
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2847
|
||||
>>
|
||||
stream
|
||||
Gb!#^=``=W&q9SYkXT:*joO<p:JN*04<#Xh8XUr-V'$D6Qr'R_+FWU[hZcMIUb#iXGAGBe-k':-&<6\'DHI,QJ+WYWIfo1h%dLG33e@)>FAjo&rY['SZ[^9m6IN4TgU/jO*61o5__^h0\;XeE.p]^ro*l6T^T:b*r-=^6C;<niXqLt&Z>GggDciODKD?%Fs!#_cKg0o_[Yk?3$Z*__=].)C(mY2[iRs[[K&TnN1NcZE(<`m!(Ad5D6mjltl3;23b?n)Dr$c4([3IG!Y0_j>cgk;/f;mT2:JgBr]m:^$-A((bdVHG-HB5hB*kdQB=#j4Dc#\\kPG\t;O['-MB<d_57PqH,aQ0<WGZ)BR:!,ArGsVq6fU@5OikK2lYli8DDp-VV&IE&m^HoPNE0`(68WZ@L[PV8#LhVqWZYj5<rLYVNe;Qna@\*"Q`Jh901;ItO"u0\Y,X$Nl8";4i^JsFaC.$tS^,2q.oJ]cUC/7Vt_H/4H".k00($YHE:OXu^4VCcVgc=/^8)XQ^J(G:7CQ4>IH@d6Frb+sL_kU47lDkLi"cIg.]M2Dj]df6rro<N7'#XC<1J3F]NUm#NK#HhDdU&$98-`jIjJ;W;\bc*rW()V>UNIbYFe,6iU!i=/W']m-`U_$g'!K1IWBUealVbA_Yli6:%U3[c5ZtD9rE^os%_#Z.?BfW&Z3tj/aBW;ul)GEr$o"3VJ_tme]=bkim14WXl5ADcbt=-)W.6.!1Cfd-Eq^^8&?l.qC!)$u8@X=$T`I8Rc=rf+NZ\SWm"QmsP_\nkqu._[0lg/VOS[peSG?s/Thl`@,(n3]j1Z$QUuTj1'%99+TTF>g\"efVFiua=EG,2[)_qGg`17:o=Y*qoY&HNtiX6$%5KX%laYO[S`7u^qB6#ehGki`dq/N:A`%`ha%R6k@S1,49<jZ\LV[`2OJm#g`eJGU4IqppXRRi>\8aGY7pfK*kGH3CUA%@8WXgWgWjT<l9g&aI'$VnSABC@SgY1`q@PmbjpCG%P/XrJe,g[2V[<'?JNrcO;K'd^<u95tq75MF-]Lo$2"i:'k,<acd.Rf(sgG.2`T^*G.*]mm>LZ_5/]GDAga9JdZ)f2U=S#9&:^Sm*=b*cBHEo2H""mgJhp0`DX[9IO@uW&0F]0S2Uk63/W%/V2>r?>>$(`AH:UK]Qq0qUjZD:gh<@N/-Y=Ds(T@p>/JMj.^8$FPV(7T^.WkX4PouBJ5>DG)2[:%dDg::iC^>%\X8i-=Di[dN?9n7Ot'B08fYrk/+An6T"BpP"D>;TSamTDS-:/'ZiS7B38*;7aO\lY[E\OY)72KNK7amUq&e(HbA'H;8slDhI_ZooT;pn1!i[iGk=/DVUtd<RsJiRC1A[\O[/r"JrN#fq+C("RN0m\SN)KRIoQ08M7@dRWjPEa&_(?.4IMM'MsGPYjpas4(4(;Z`^p*daU(I]Ue#]=9_na5eK9nJ-Q%po04acdf'j6=P!DPN)*T[E+m#,RjEGe\\jFk9\"B:V'j=fN(_r!1rImBWNK95;/@-a>T&\^>:pK*R1:sbeXD)<TUDqJhK9"#d[9qFaS;sDe:DR:iW5*8\]"@bYBWqeQpiZb-OF3E%+W8G++T9qa^Q.8f9k(,:!uYu&fYVTCM@fgRPgnAb;R8Y\c"[9A]s`Ge'd[].@Um`>8V7P;4Wi77-LpNdWeK',>h*pj"cJ5b9$p>Q;!^NAFoQZ][Gm,kHc:"JkpMJKe*Y\@S@'O^JkTuC8U5X[0q#pB+V&miihu%bZIK:nn69?%^GHs*E=LB7pYKj>J[#X4<8d(/dU'Kj,G^_mirGfm'6_mLP&VCBTD*MU.&Ylc.^r0@PlJKR6Yl==pp5A6!sV>l'n9aeRRN"3LQb)HoVg`75/F_mB8B[&)DZOYDh2T!qkg#=>o$;G)XR'?'*X/:/$,=7\;Z.gc$8VX<4.ncWTthiYXS?DU%V\7o2QQZZ@tP"Z69,_5ha[0T[*]-c[DC+E29bXYi+_IHT2GgNDYTsE6Mj::RPs4XN&2u,6'/Y[<lLML@$B)iajVa)&>+5Ktq@a]k&W'ICW^Z_TD\!^K$43r$73L$S\3E<9rcT\3(!V]P`ks?U![m`tReDeh#"b`Oe\d'$h_/CLFo.WRafZ$plFpZ!_>r0P=\I8;XDNhr!9]+0t1F#hZYX*BJ7%%\(_dmFQsNOC#C&P4Z;[rX?GV3N>(/l&c$(*$nCl5_1I-;SL8X]?Nh6ro1NXkf'Wlb4L%+djGZ%SF?uI_LJnM*!\-)Nf#bP?12Ft2!Y.];a=s'_,fcZd&[1M>29pATaGMB]O`OI_/G!>NLBEH1`LmZ<-9-W(c/rSF6(!Gf^d'?Md^gDaBj)Fdb=A:K:q'1JpRGc`uAHXhMlYu!9MlSh&!>PU(a^N$jk<2J[(#>@saN<EpbCSA;`-crh+Jj`U@YYpohj&Z0]FJer%9;]&UTh0`D@a\Z+q)kqY^$kB#4Fes1[>ZYD4eAH,"DdU\Qn+p%[(&X]e\B/re&YQ&tHf^Z^3r[U*Ac`&06heiW8FNi>@HkWEZ\0o7:/]*%3&+We"j@nfF\Fg\"I%&CM@#=eN?@F'..7tL06S=DJfb]\6$Lt3@Jtj8k,qq5_*<i;kb-0shmop4iL7!is)*@)jjYIXiYCO`W$W<(AO?F)XXIHUO:U5?fA>YQkO1,d6'C=Ke5$]e)."GZ_E]_&oc$8VK\bU8/]rHIiSs1X3*=<Hq5?MhVaTUAhjCs.^SfqHP=$td/0[U/l@pSsqRnWjrq(S6(?XRk$MtDtNl1+`JG.5N-PK+gamh5FS?fg,9/+2ZP?0,#AB^G/[;iY#-mtB^mG&;G_+SPqIO.D3L0$3ZNI=Ij[\FeElVSUr8!,G9rir~>endstream
|
||||
Gb!#^gN)%.&q0LUoV43PP6?-\b`e73'@4[T7oJKk<@N`W,UO5)+\5u6laK0B'IiW'HagF_Nm+0L(SK1=1ggA'IRs19]*3Go!;2c\Ts.W"0ba^*T4,;jqr`q!P(or<Ni<n(gU<j@'*I.0@=43%\d3l+(+>G$X4K1T?=flM`+q=[<.U*X(IZSR*[^>`nH0QA82GbSk?uGQci>f:U6CH\7<>DFlCd>S9=#X8Cm.U&Jr7l@q3k`bNt[3[C;D4D+m-$*"a.s`+et5J)'r3p_!uX=(3(gB@<;sI-e8T]*PL'7>AH"MUXsI2p$1ZGrhB@oH,t#^Jt8_LI??cU:;=MMas&(qfgB?APStlH_>-gB\#jpBFfJQ4Fctb!=ci2!c.0;bDWln0C'+m-WkitlgdYTgE7hF+1E`P\-B+s]-$+S,cr8CQ\.kD\:3ak?<_I*mZc>Nij4btBT!npm-VeH&R-^k@meNP9XSq3=<F'<7:J)>)<>Kn#jEiKmanK'\rSVSA&6S2R3S^53r^5r(\L?V;ra!PY39Jj2\=V^3]RL+mbqBg?9n:A"<t0BuGj*/n]0qqSW'Vr2UcDn?6F`V17VU>V;8/ZS(V+N"[9)TJdQuT&M5ibD=FGRE<.i8'_'TeO\k18?:t]_6<2"KR@SXq_'!D3&WBUftF2.*`pIMWW@@<Q%:fdMbYZ?VerS<J](M,e]A=*5"QHRE#B'W2ce3P1i=mnQ1?!cFEp4kh<opfm(O@<"H9L+hYGRTrpEqLQ4_;h-UZ@mRO&UssM`&r9TEQl9fF9G0pZ(BaVfOl;`pBO9Z(e]!=P2d!3G;jom+Q%?f#+sqmI32Eoak0Fe+^5-QMJMC(CJ<6]4!(^2=2p5C4r7$2`s[^P:r(Xhj1$1b(Z:'dOEr==AL\CNGt@"`bJe;enk]2ZEMb_F2G=1mhm5Pm'GDN\Y-pK4$2GFR4<)_K"'Y*,[>J?\Ud^F,HIX0d/;K>6<JU(K(d]9F)8W4[!4c9E!2#,L]B4ZmE7q0-0b-+'HW::>W>Ilm<>iTMr;]8)S?9E1<'D#$rcOGO,u;"KV:[]J[W\tWYnWW7*,c3i.a0*2amVBZGNsP899^S!q^;&HmZk<Clu?`nM8k/'L<5nR^]NE:EXRO>qn(p65NMtEqiZFF4FZd*/ekQN<gnc"+$_FY^&Xbl(/JZ#09neF`R*?e_K8='5.704'ps&$NW%qK\",7XE@T6*FYfK;bMaK=":fAEG=`Q1U$'cM]mAol4VI#IOa;?aB*_eAFTr4)OmW3u2d5`&I8SdTG9"%K)PXb],FJZ]2l[_3j2RQR8<Ng+G_QY@)[).6"\[-M*im.bGN5GUUh?`r)oTA/X.A?`9G6Sq\dWs&q/dDT*T-b=B@m*R)l0ZMHGNG7,D#h#3?*O&`G<$D8T\3I$+1LBIT5L/M7@dRWjPD6&QE9mgmP%%M%cpco9La4$U$Rh@jM*CjFkh2q*qq/@a]V5WpYDU5J_kDGp0L9ZR5C..1(YQOlGEl)3QroP5O`l6<@i2FSkko>,/LuJFeE@Wk\$.V"$aXL/f,pomQbM$n@L3?=juhM@Jk[CimSj@F,c-XnI[!Q-k^>qT6Xu,nmd>=*HU<"7rsM#iBMB'p9)K+XBGN'r2pul.7XpX\MPID'6_J"^;g]71nO/Q'0i+8m5`Vp43l]2+h[*(bqb/Q'+lPPdJGcWE#YDF`uTc<HG?J\Z0)-$5O;gQ(f.^Tieijlh^'ACWRG%p57rsdSCXoVq\6_)/DZ'"I[oPPHsiUA%I2;8o=>__f*GaVs?4_i*0Xj^GIL934`P+H]Jh;#`N>^L&t_KP4l2X+!Af2(4$Pe>JHqF(5u2$[#3Z\b%W^S$.g#a8P``5s0'Yn*-0P*,B0::X:I9])QicZ?a2FI<]urm)P`?3DV5\NRsg$AL[-+]dGlfTc#DMRe>:>48CrKB*cE7c0Q2Zq1C9DaYbW/?*K-pu:d9(9V(q<J^A"T)QH4H=HG42=d#9N3rrnLUB]-..!VbbpS8iMDZ2%foj0A;s(q+0L/XAC*KM/jf%'HN`CF;"erEfS?mQrFqlDC>DLmP6"U`0RO\\Ml@^#_iP]5kSQM3I]&IOc9p\5=.k(LT?gWTMYC`,Zr1n@CueJ,)@f/rm.Tf^L^Z0&E3W,4QWS[Q=DBWRafZFJ>*'A&6_q@)9IdOTJZ7cJQH/+0mf]3nFqiBY7gB]8pf^c(c6H=+]Lp?FLE-m(&nj%(pFk<#iHMT`>Nlace>R+:X*jD`[m3ZgkJ)9k=+6]]kWL-]I%:$iGMGM[ThD@"F3E/D2#gk#pC=N2M3MI#G,R)kg@5?+%6sqEbqFCiRWr*!X.iT%2,^#G]uA>GJLY#lKbd2OPf9]CR=uh!bmKQ\PE\[k4^f?T/7RD@e)l(EIFIFefuB./l-03k?tZkR+#IFtf_FPQG>,_nu*u:#rj_E2+Gjh"_':\;N"WP)6#;V9Pn(`>[<b_"YdUiNjX*"GGPuG-PkK+T$4ihOW4&qi4[5q'>hZ;NXB%X;2+]2j(n/>s?F6Q1k'(Y`Bh)/*"C]:%k)J,4b,5J3hA+D88)=_Xi+Pgt0F;3;Sa8PmUdPr<nr56\eHq`>u%AhoNto,WC!?K9[O:Aifh+;6&!"fhQLBY*B&ue(%K,`e3Vm?N.XW!,mr4fqks;VCal.62^;`4E<^po&0,[!L>UL'';cORmCXE^%ut8<OUc@/`F"c.LJ<52(uH;Y"]<b-XH[^IkH/[Sm@UF>Yb`l0TUpeGrSp0&'4t/`R3-7LE/`Wjf9Au)T?_j[.!VNm-gDSe+mDRo/g*NB2"m<hk0B2CCG4T3$dO$rRK@Wqhf08F]\F(2&c?RkTn/!(uU&ofsR)`4#m*J:hSJH~>endstream
|
||||
endobj
|
||||
88 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2061
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2091
|
||||
>>
|
||||
stream
|
||||
Gau0DD0+E#&H;*)YsZtpUunI7]n#<k1,pG^1JU8eb]K_h*[)#T/L3b4*8Dh>]mGnB+X7)\<\g>smHrZqZb'iW.c*#XG3/c@"&RiPTF;#<TK:J/E"/ASak%2p;<e??Rtdq1UY-oIB'Hf8Vf<kco4#Xg_&TC'J$0NG:!$4+mUusX^?\QX%em:p`))*&/J94"8Q6F8U+VcXLo/in[^o(3PUq1/mTtJ3O!3Lm2r[R@@V/+MiY`!C\?PmT#jlj`5a%(3E>uE:ge)5a0;XZ#n_RiT^9R:]DT!ng1q2M4O1TRGT<(`?G(?Hd*frjZno5jr\;'9"N^M2lP_+mn5SZPh6W^grb)G(]4G3<lfjS^')D]38HZ!U'hY06_KTsXip0ne*1&%<dI`5\sIFf@Pf*.3V+Gc]fWBWpA%r48&IZ9T\_HT]`P0fgb8*]7YF?dL_F&)NsnN*,FVQro%%BC)5nmr4.[-JC5DZ07a4JV&bND3!?m#2UAYQesc38$l2>0Sq@Tn0#gP!F?\\_N5ffoDC`Fo<VTn[=('rF'R;lpYQ&VL."@GrG8X\Ll5_EF6$?-,C<c_=HP3["X$2;r(J#=n;.Ym;i.[?]o@Dmaq4u]Cm@UL0;UXd5,)'*2cHjAH;M?ge_T=b(KmB:Q$`+^'7cWB4\;&/X*-5AsL96Bb2L634]U5mBg9,n3$Ko]1R\*j^X)K\EFX-&r(=uT"m9$ZZLcb''i:#8kU!F:N.jL4S441&AB*<4<E(pr\A"BN3saf2Y.!p/1;E,%lY0Rj.`L2)NW!b`AA)r>*peF%MpA9Fd-a5js4?o.kXD66'4p.p*A@T\p][$(nmh'Q,ecbXpGG1Uf@2PZ.+CW)<Y9r/HTeE/V!+>TaaB/0Te"4&"FcP)r]r[ob]lpV=.1SCmFII:@9]]h]?4P,b2pb0uc\kF,/HtW'`_k6Te8p=eUfM4LgOM&M`%DR6e6gAqu[!WZ!1kM(_4q#;4a/cM8+_c\egQmWEj=&&BHia3d5He9W+-Bgom)]8'PXr,fnubU7'gO,2+ljW9m&`=)UCijo'E.r"uC4^G<`fHe(/T85[1?:`n\gT='SD>fO&^hp9s*+"3T0UI7SQZ2d^n8TYT%j"'o!]pf&$?kb?^2b_*s0T"<%&_u5.'(drS5rsl(?3eGA]1iJNRHFXLQ^8/N22oAW^!t0lX,,+%<a^8Z0@')g0!OX`5J,A$V[:&%V1,j'N;7B"(1Q*6E/M0ItcH#*%UL\V@a?4hLX.jLXjSaT_mb18.B;CCLn!Y#GB$,&=^D0gY;EBG(P"A,fM:mrkm&:_c6>p"+sLGX4/\S\=oq@ap="3fpaNGcS>E&Yej9n]ZIV[+?\o@lDY&f;D!%?>SA*FLmX'VBKX?6qf6h0-J1VM$.>WB\k5pB>>lAdb>f*SX<;GkisOR`/7<CB(ch<nfW`pVVX,GdOW@q\:CmqUgUF)./j!(Z^#q^OSNNGXc>g20kY^cgF'=^-06[CA^)n(AhnB?9NlJB=g@OA2Lu6:LLP[.n-cb2uK.:iFE';D__qH"'5n*J0K;u:#CNG:<&*_'(l^&D-'6dNQ#&$^]k?O`>R:^+t1s8i;9csmt=ZVhqkMn=MiN?:9;.`-8I?1;Qa\-.nmu.CMq[O7%Yg]<@Rk6t\I=1SP;^BJYT;@g?Z:?9k>YLbX;i7E1JX$57mDo]5l9M4$@]JRs3%s88!:u+[9U$L5RKnYDH#Xp2N*SjV49dX<=ZHq/Z/<-X\AqLK'P:MFZ[nP3gc1EJAiBe/FWKOWNs*3`n"TclfK.\@(DKH+,1R$fV+PNR!I^bbY$I1K&WhG'Sc*Fd?BE+t':Q7;q/mM$PQUUoGSD#;Y`B%YfCSo!Ha:oj4aq9E8<YoQK0o+;p[/1Un$c<*Qu>6J,7jXmKURCoDS9(0F?,67CbXE#-?->\^entNs&Kf#2f]Qk8gbI^;C>%-lTll^F'2YoRO2`N(+Utu>/MU:,<5P0o>,T^Y0XpUr^e<<-kt#N4-`,=3Kn*igL);25?iq6-#*h`&GY,"UQ_62&blAtjN9e<cNPpkd[b$A9b1D)-YFT=i8e6(BO=4~>endstream
|
||||
Gau0DHZRn1&HCX[@Y^gjS;*5LH@Y4(1-!+T1JU8eb]K_h*a'I*>=aWH34MPoHh[.4ECL-p/Js,IHi(DQmI!OJ(#N3khjjk>%7*#+5bg$'J7]>pL0)_B*dp3"E_aLt<m+GOBJu>JeSPE?+W2)2.p8;F_`:OC(+\Q)]q2$!(#g[l[2+=B\4F8?$DVnY`cENtmb-gb"GXB>Hmpn$`qMBn(+&*dX[j"<2-#$2V1,d0oaXflces6fncVirauP=n9Qa)Wl>fJ5Ga]<:eTFc/delS4G6q"uL7!t6R(3OmR1hAIAdj?#C&.3/3Q[54?l==h2Y;Ts]n`5%mH:[dQqc!O-#D@X@cs\/Beob#Sjbn(AVm#m@D`i0B1Wa+[\?;VU&fB:`JH_=?;kI[nWOil?F_0?VPk(aX]=F?..D]Tkp'_:[YX+Q0MYa>I.;o@;>jdE#[\B!IXj,,hP\1D<P?\LP1HhAnt#Z9kt_q#Y!#/<k<AN%^+2k%jFR4i=L,*XFUF,n0q5I[)uapt'=D9Kp"aqj=[+3#Cs<WYd&,A.H*W^OC=3j7^56B6'oDS&pK3M>nf;)Re-hT+`d&@<#G[LMe?eQIV0C<;75g3j[Abp[#[b13(!.30iUYqgE]Aht9AMEN?1RRUmIGi`>EWY8mC)dSHgO)8%',K9ib+#83C+J&b5q*^KqZY%e-RQnSlSptHj)>UAlZ(@ROV1>]2cbk[#(2,*8H=p]5<odHlp)p0@OJ)fGKb_cFS$t_Xs,i$%2?K8kWt:fQZ%Gftn;GS72;'OWQae&%j<"alnEEDF9^uYMsN*RqLr^A7uT?>YdJSES82D*n0FABGlo#7$9u/M]:PpPtdd71go:+Q__#SjqFj[c*jutmhI(*BU5c%k6_\s"iI$tP"hU%]+p:<-$3N_.2MJCm"673G;o<MD4YIr`*-m4?LVZ+n)Y\d(I@!7i69-qfS_H(DB/B\X\mH"7UCjPXCP;5N7g>+(A8>(gMNA+C9B,B[l%m"=j`_k1.$'?X>B(34u)O@Cu%k$gHU)l(NC[V(EBh@Oq:o_)2FbH<kZuB6KFM.d!lgpJ0i?$%O$]O##iM8K"KXOrDW*n&<ViAGH3IW"/rmE[V_%/[0kd1+'F6N>a#E*#,>%pr&-1cpe;SJ+__?,,1ZrGM@^+S;hopEbJW7?=tk]kmkrTpCY+S^0/=(.f;mU7__F$oDJ'PRD^PjslB\ILh"5H&nYnb.&o>P7DQ'Us%;&?dm('mH,S'!&(>)'\'_<^]3*R8bH-HS\k\VfS#!ZK6ZNqIT3<"8(^DIH-5H_lIU%[]G._q#TP[kiod$ILSS$Bk&Y\]T_Gg3pOfI9jgg9,U,ZqOr-RT0Z6UM19$B/g`=DtCP&a8B^?JYi#03oBN,n:[R.ML__8dSO!R#8SL'1SHUrj8OJ;gqscLXAku'71D)lY\]99[o*I)EX-m5>`[pB@fq)4a3;(A5Y!2=\R^I-Mk(":H\u3u4+T2!F"sZjGc]sMlbr$]3lfJ\2ih]<T$q5YJ_0u7pjU`"!p:V0-Q\3FlHI^%+T=kSI4PL-rc2;!1T1@f$dZk^0fjR`6(!1$6r4Wt@rIC3i#t>"o7H7^A$tg=XdPhdH7??lD,K-=J`sP=2HrG8-FF:WA7'l19T$Gk'<4n;)Amf`KhbsK8Fau.Ye+g^+fRF'jR7I.o'V[!GFr>9+#VEH'5R6fEs>mN4c-$E9=Y$a"]EAU'r!I6<2[Z4X_143,VfACMa^B?4$m.uW7/oS5'WI,MF_tcBZR2oQJ?ksGD3WK>3lZ0q=q:NRH%VcZ,:5+*7K%qjOoIp^Hqn$o0sdl*)R)S<^l.#J6?Tn%[bTJ#3X.h3?A4?kL%s'QGXFD9KurG4]Ga+&Pj;&>_,D-nK.S;LO>jJJ%[5!Eo5p?p5$(hdkEY@^\dJ[Yl9)%Wl9>p>.jE7O:p(6I<FSY]&kM_C_q`#VF85#Jq-oK!r`o;X;`Ge?=a73m4:dpe*8./oXUtRi<Vf>[S87kUi*2cjpVJ:G<S"SbR58tksH)D9gM*uQrKjsnk,GJDSmERo@\^[C/GqkLG83>*hDZ9M'*'i+BLBlEA$_tZ>MhkXCk*"+fCP_E:bf[^j;4~>endstream
|
||||
endobj
|
||||
xref
|
||||
0 89
|
||||
@@ -582,15 +582,15 @@ xref
|
||||
0000011129 00000 n
|
||||
0000011232 00000 n
|
||||
0000012440 00000 n
|
||||
0000016058 00000 n
|
||||
0000019295 00000 n
|
||||
0000022787 00000 n
|
||||
0000025731 00000 n
|
||||
0000028677 00000 n
|
||||
0000016061 00000 n
|
||||
0000019298 00000 n
|
||||
0000022790 00000 n
|
||||
0000025734 00000 n
|
||||
0000028673 00000 n
|
||||
trailer
|
||||
<<
|
||||
/ID
|
||||
[<43846ba034f0f13d3c3b73c3a2995bc3><43846ba034f0f13d3c3b73c3a2995bc3>]
|
||||
[<551952d3ad7bea00713487e65aa30a36><551952d3ad7bea00713487e65aa30a36>]
|
||||
% ReportLab generated PDF document -- digest (opensource)
|
||||
|
||||
/Info 48 0 R
|
||||
@@ -598,5 +598,5 @@ trailer
|
||||
/Size 89
|
||||
>>
|
||||
startxref
|
||||
30830
|
||||
30856
|
||||
%%EOF
|
||||
|
||||
718
docs/specs/20100612_1_scaling.md
Normal file
718
docs/specs/20100612_1_scaling.md
Normal file
@@ -0,0 +1,718 @@
|
||||
# Platform scaling and multi-environment architecture — specification
|
||||
|
||||
**Document type:** SPEC
|
||||
**Source draft:** [docs/drafts/20100612_1_scaling.txt](../drafts/20100612_1_scaling.txt)
|
||||
**Design review:** [docs/DRs/20100612_1_scaling.md](../DRs/20100612_1_scaling.md)
|
||||
**PDF:** [20100612_1_scaling.pdf](20100612_1_scaling.pdf) · Regenerate: `bash scripts/build-all-docs-pdf.sh` (Mermaid → PNG diagrams; requires `npx`)
|
||||
**Date:** 2026-06-12
|
||||
**Status:** Planning / brainstorming (pre-implementation)
|
||||
**Severity:** Strategic (post-alpha evolution)
|
||||
**Scope:** FE/BE topology, service decomposition, dev/staging/prod clusters, cloud migration path
|
||||
|
||||
**Documentation index:** [README.md](../README.md)
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## Table of contents
|
||||
|
||||
<!-- toc -->
|
||||
- [1. Purpose](#1-purpose)
|
||||
- [2. Vocabulary](#2-vocabulary)
|
||||
- [3. Current state (baseline)](#3-current-state-baseline)
|
||||
- [4. Goals and non-goals](#4-goals-and-non-goals)
|
||||
- [5. Requirements](#5-requirements)
|
||||
- [5.1 Must (platform)](#51-must-platform)
|
||||
- [5.2 Should](#52-should)
|
||||
- [5.3 Future capability slots (normative intent)](#53-future-capability-slots-normative-intent)
|
||||
- [6. Target architecture](#6-target-architecture)
|
||||
- [6.1 Environment tiers](#61-environment-tiers)
|
||||
- [6.2 Service domains](#62-service-domains)
|
||||
- [6.3 Reference cluster layout (prod)](#63-reference-cluster-layout-prod)
|
||||
- [6.4 Traffic and control planes](#64-traffic-and-control-planes)
|
||||
- [7. Service inventory and scaling profile](#7-service-inventory-and-scaling-profile)
|
||||
- [8. Deployment and promotion pipeline](#8-deployment-and-promotion-pipeline)
|
||||
- [9. Zero-downtime and load balancing](#9-zero-downtime-and-load-balancing)
|
||||
- [10. Data and storage](#10-data-and-storage)
|
||||
- [11. Simulation lab (Alpine HVMs)](#11-simulation-lab-alpine-hvms)
|
||||
- [12. Capacity planning framework](#12-capacity-planning-framework)
|
||||
- [12.1 RSSH / remote access connections](#121-rssh-remote-access-connections)
|
||||
- [12.2 Cast and media paths (future)](#122-cast-and-media-paths-future)
|
||||
- [12.3 Network bandwidth and egress](#123-network-bandwidth-and-egress)
|
||||
- [13. Cloud migration (GCP / Azure / other)](#13-cloud-migration-gcp-azure-other)
|
||||
- [14. Global availability and GEO policy](#14-global-availability-and-geo-policy)
|
||||
- [14.1 Reach model](#141-reach-model)
|
||||
- [14.2 Segment isolation](#142-segment-isolation)
|
||||
- [14.3 Enforcement layers](#143-enforcement-layers)
|
||||
- [15. RBAC and operator model evolution](#15-rbac-and-operator-model-evolution)
|
||||
- [16. Commercial and paid services (platform hooks)](#16-commercial-and-paid-services-platform-hooks)
|
||||
- [17. Observability and SLOs](#17-observability-and-slos)
|
||||
- [18. Safety constraints (current topology)](#18-safety-constraints-current-topology)
|
||||
- [19. Phased delivery map](#19-phased-delivery-map)
|
||||
- [20. Out of scope (this SPEC revision)](#20-out-of-scope-this-spec-revision)
|
||||
- [21. Related docs](#21-related-docs)
|
||||
<!-- /toc -->
|
||||
|
||||
**Documentation index:** [README.md](../README.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
Define how Android Cast evolves from a **single Alpine BE HVM** hosting all backend services into a **multi-VM, multi-environment platform** that supports:
|
||||
|
||||
- Stable **dev → staging → prod** promotion
|
||||
- **Horizontal scaling** and **zero-downtime** deploys where services need it
|
||||
- Optional **cloud** footprint (GCP, Azure, or hybrid) without breaking the existing Gentoo FE → Alpine BE production path
|
||||
- **Network bandwidth** budgets and egress planning alongside CPU/RAM/connection limits
|
||||
- **Global availability** with **GEO segment isolation** (worldwide reach by default; denylisted regions per content/policy — e.g. no publish in Zambia)
|
||||
- Room for **future RT/AV**, **VPN**, **payments**, and **third-party streaming** integrations described in the draft
|
||||
|
||||
This SPEC is **normative for planning**; individual services retain their own SPEC/DR pairs (URL shortener, remote access, SFU, etc.).
|
||||
|
||||
---
|
||||
|
||||
## 2. Vocabulary
|
||||
|
||||
| Term | Meaning |
|
||||
|------|---------|
|
||||
| **FE** | Forward-End — Gentoo bare-metal / XEN host; TLS, public nginx, UDP DNAT |
|
||||
| **BE** | Back-End — Alpine HVM(s); app services today on `artc0.intra.raptor.org` |
|
||||
| **Cluster** | Named set of VMs + shared config for one **environment tier** (dev, staging, prod) |
|
||||
| **Control plane** | HTTPS APIs, consoles, heartbeats, RBAC, orchestration metadata |
|
||||
| **Media plane** | UDP/TCP cast, SFU/MCU relay, ffmpeg transcode pipelines (future) |
|
||||
| **Sysop** | System operator (PO today) — owns infra simulation and promotion decisions |
|
||||
| **PROD** | Production cluster serving end users |
|
||||
| **RSSH** | Reverse-SSH remote access track ([REMOTE_ACCESS_IMPL](../REMOTE_ACCESS_IMPL.md)) |
|
||||
| **GEO segment** | Country/region group (ISO 3166-1 alpha-2) used in allow/deny policy — e.g. `ZM` (Zambia) |
|
||||
| **geo_policy** | Per-resource or catalog rule: where content/API may be **served**, **published**, or **streamed** |
|
||||
| **Egress budget** | Planned GB/month or Mbps peak per service/link; triggers scale or CDN move |
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state (baseline)
|
||||
|
||||
Today one **BE Alpine VM** co-locates (see [20260608_BE_SERVICES_and_infra.md](../20260608_BE_SERVICES_and_infra.md)):
|
||||
|
||||
| Domain | Services | Stack |
|
||||
|--------|----------|-------|
|
||||
| **Web / console** | Hub, crashes, tickets, graphs, builder UI | nginx + PHP-FPM |
|
||||
| **APIs** | Upload, heartbeat, remote access, graphs ingest, short links admin | PHP |
|
||||
| **Git** | Gitea mirrors (`AndroidCast/*`) | Gitea + MariaDB/SQLite |
|
||||
| **Database** | `androidcast_crashes`, `url_shortener`, Gitea DB | MariaDB |
|
||||
| **OTA** | Channel JSON + artifacts | nginx static / PHP |
|
||||
| **VPN** | WireGuard lab (`wg0`), RSSH bastion | kernel + sshd |
|
||||
| **URL shortener** | `s.f0xx.org` | nginx + PHP (submodule) |
|
||||
| **Build** | Docker builder orchestration | Docker on BE |
|
||||
|
||||
```text
|
||||
Internet → router (134.17.26.161) → FE (10.7.0.10) → BE (10.7.16.128) :80
|
||||
```
|
||||
|
||||
**Constraint:** This topology is **production truth** until a phased migration explicitly replaces it ([INFRA.md](../INFRA.md) §1, [bottomline_reminder.txt](../../bottomline_reminder.txt)).
|
||||
|
||||
---
|
||||
|
||||
## 4. Goals and non-goals
|
||||
|
||||
### Goals
|
||||
|
||||
| ID | Goal |
|
||||
|----|------|
|
||||
| G1 | Split services by **scaling profile** (CPU/RAM vs storage vs RT media) |
|
||||
| G2 | Three **environment tiers**: dev (unstable), staging (beta), prod (stable) |
|
||||
| G3 | **Seamless deploy/update** with rollback per service |
|
||||
| G4 | **Cost-aware** sizing — no oversized DB VMs; burst CPU for build/ffmpeg |
|
||||
| G5 | **Simulation lab** on Alpine HVMs before cloud spend |
|
||||
| G6 | Provider **L4/L7 load balancers** ready for stateless tiers |
|
||||
| G7 | Preserve **dual-remote** git flow and existing public URLs during transition |
|
||||
| G8 | **Bandwidth visibility** — per-link and per-service throughput metrics before saturation |
|
||||
| G9 | **Global reach** for public services; **segment isolation** when PO denies regions for specific content |
|
||||
|
||||
### Non-goals (this revision)
|
||||
|
||||
| ID | Non-goal |
|
||||
|----|----------|
|
||||
| NG1 | Alpha release blocker — scaling work is **post-alpha** unless PO reprioritizes |
|
||||
| NG2 | Immediate full cloud cutover |
|
||||
| NG3 | Replacing Gentoo FE in phase 1 |
|
||||
| NG4 | Implementing payments, SFU, or DNS resale in this SPEC — only **platform hooks** |
|
||||
| NG5 | Full **multi-region active-active** prod on day one — phased via CDN → hybrid → regional PoPs |
|
||||
| NG6 | Legal counsel for country block lists — platform provides **policy hooks**; PO owns compliance text |
|
||||
|
||||
---
|
||||
|
||||
## 5. Requirements
|
||||
|
||||
### 5.1 Must (platform)
|
||||
|
||||
| ID | Requirement |
|
||||
|----|-------------|
|
||||
| P1 | Every deployable service has an **owner**, **health endpoint**, and **config outside git** documented |
|
||||
| P2 | **Prod** changes require promotion from staging (except hotfix path per [GIT_FLOW.md](../GIT_FLOW.md)) |
|
||||
| P3 | **Stateless** HTTP tiers scale behind a load balancer; **stateful** tiers (DB, object store) have backup/restore runbooks |
|
||||
| P4 | **FE** remains TLS termination and edge routing; BE/cluster addresses are **internal** until cloud edge replaces FE |
|
||||
| P5 | **WireGuard UDP** and **RSSH TCP** paths remain explicit in firewall/DNAT docs when split across VMs |
|
||||
| P6 | Simulation cluster MUST be reproducible from repo scripts (Alpine + package sets) — see [§11](#11-simulation-lab-alpine-hvms) |
|
||||
| P7 | No single VM hosts **both** primary MariaDB and **unbounded** ffmpeg transcode workers |
|
||||
| P8 | Every **bandwidth-heavy** service (OTA, object store, SFU, ffmpeg, CDN origin) has documented **Mbps / GB-month budgets** and alert thresholds |
|
||||
| P9 | **geo_policy** model: default **global serve**; explicit **deny segments** (countries/regions) per content class or tenant — enforceable at edge and BE |
|
||||
|
||||
### 5.2 Should
|
||||
|
||||
| ID | Requirement |
|
||||
|----|-------------|
|
||||
| S1 | Use **managed** MariaDB / object storage in cloud phase when cost ≤ ops time |
|
||||
| S2 | **Blue/green** or rolling deploy for PHP-FPM pools and Gitea |
|
||||
| S3 | Central **secrets** store (SOPS, Vault, or cloud SM) — no secrets in git |
|
||||
| S4 | **Per-environment** DNS: `dev.apps…`, `staging.apps…`, prod unchanged |
|
||||
| S5 | Connection and CPU **dashboards** before scaling RSSH and SFU |
|
||||
| S6 | **IaC** fragments in repo (`deploy/`, nginx snippets) for each new VM role |
|
||||
| S7 | **CDN or edge cache** for OTA/APK and static consoles before scaling origin VM count for bandwidth |
|
||||
| S8 | **GeoDNS or latency routing** when a second region/PoP is added; single canonical URL preserved |
|
||||
|
||||
### 5.3 Future capability slots (normative intent)
|
||||
|
||||
Draft “future key roles” map to **service domains** (implementation deferred):
|
||||
|
||||
| Slot | Description | Depends on |
|
||||
|------|-------------|------------|
|
||||
| **F1 Multipoint cast** | MCU/SFU relay outside LAN; TURN/STUN | SFU SPEC, signaling |
|
||||
| **F2 Stream transcode** | ffmpeg any→any; RT and non-RT | Media worker pool, object store |
|
||||
| **F3 Cast storage** | Recordings on BE; paywalled retrieval | F2, billing |
|
||||
| **F4 Commercial VPN** | Anonymous RT VPN; capacity planning | WG pool, payments |
|
||||
| **F5 Payments** | Low fixed cost at scale; usage-based optional | F3, F4, add-ons |
|
||||
| **F6 Mobile add-ons** | Masks, background replace | On-device or cloud GPU |
|
||||
| **F7 In-app transfers** | P2P or ledger between users | F5, compliance |
|
||||
| **F8 DNS / subdomains** | User subdomains + site builder | FE DNS API, tenant isolation |
|
||||
| **F9 3rd-party streaming** | Ingest HLS/m3u8 or RTMP; re-emit as cast | F2, cast handlers |
|
||||
| **F10 GEO enforcement** | Region deny/allow on publish, stream, download | F3, F5, edge CDN/WAF |
|
||||
|
||||
---
|
||||
|
||||
## 6. Target architecture
|
||||
|
||||
### 6.1 Environment tiers
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph Dev["DEV cluster — unstable features"]
|
||||
D_FE["Edge / FE stub or shared"]
|
||||
D_SVC["N × Alpine service VMs"]
|
||||
D_DB["MariaDB (disposable)"]
|
||||
end
|
||||
|
||||
subgraph Staging["STAGING cluster — beta"]
|
||||
ST_FE["FE mirror or path prefix"]
|
||||
ST_SVC["Prod-shaped VM count"]
|
||||
ST_DB["MariaDB + anonymized seed"]
|
||||
end
|
||||
|
||||
subgraph Prod["PROD cluster — current + expanded"]
|
||||
P_FE["Gentoo FE apps.f0xx.org"]
|
||||
P_SVC["BE VMs + future workers"]
|
||||
P_DB["MariaDB primary + replicas"]
|
||||
end
|
||||
|
||||
Dev -->|"promote on PO sign-off"| Staging
|
||||
Staging -->|"promote on stability gate"| Prod
|
||||
```
|
||||
|
||||
| Tier | Audience | Data | Deploy freedom |
|
||||
|------|----------|------|----------------|
|
||||
| **dev** | Developers, agents | Synthetic / disposable | Daily; may break |
|
||||
| **staging** | PO, QA, friendly beta | Anonymized prod subset | Weekly; feature flags |
|
||||
| **prod** | End users | Real | Hotfix + scheduled; zero-downtime target |
|
||||
|
||||
### 6.2 Service domains
|
||||
|
||||
Logical grouping for VM assignment (see DR §Architecture options):
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Edge["Edge tier"]
|
||||
FE["FE nginx TLS"]
|
||||
LB["L4/L7 LB optional"]
|
||||
end
|
||||
|
||||
subgraph App["Application tier"]
|
||||
WEB["Web + consoles + OTA metadata"]
|
||||
API["Control APIs PHP"]
|
||||
GIT["Gitea"]
|
||||
SHORT["URL shortener"]
|
||||
BUILD["Build / Docker"]
|
||||
end
|
||||
|
||||
subgraph Data["Data tier"]
|
||||
SQL["MariaDB"]
|
||||
OBJ["Object store / recordings"]
|
||||
end
|
||||
|
||||
subgraph Realtime["Realtime tier"]
|
||||
WG["WireGuard pool"]
|
||||
RSSH["RSSH bastion"]
|
||||
SFU["SFU / MCU future"]
|
||||
FF["ffmpeg workers future"]
|
||||
end
|
||||
|
||||
FE --> LB --> WEB
|
||||
LB --> API
|
||||
WEB --> SQL
|
||||
API --> SQL
|
||||
SHORT --> SQL
|
||||
GIT --> SQL
|
||||
BUILD --> OBJ
|
||||
SFU --> FF
|
||||
SFU --> OBJ
|
||||
WG --> RSSH
|
||||
```
|
||||
|
||||
### 6.3 Reference cluster layout (prod)
|
||||
|
||||
**Phase A (minimal split)** — cost-conscious; matches draft “tighter” intent:
|
||||
|
||||
| VM role | vCPU | RAM | Disk | Services |
|
||||
|---------|------|-----|------|----------|
|
||||
| **be-web-1** | 2 | 4 GiB | 40 GiB | nginx, PHP-FPM (hub, crashes, graphs, builder UI, OTA JSON) |
|
||||
| **be-data-1** | 2 | 8 GiB | 200 GiB+ | MariaDB primary; nightly backup to OBJ |
|
||||
| **be-ops-1** | 2 | 4 GiB | 80 GiB | Gitea, url-shortener, small object cache |
|
||||
| **be-build-1** | 4 | 16 GiB | 100 GiB | Docker builder; ephemeral |
|
||||
| **be-vpn-1** | 2 | 2 GiB | 20 GiB | WireGuard + RSSH bastion |
|
||||
| **fe-1** | (existing) | — | — | Gentoo FE — unchanged |
|
||||
|
||||
**Phase B (scale-out)** — when metrics justify:
|
||||
|
||||
| Add | When |
|
||||
|-----|------|
|
||||
| **be-web-2+** | PHP-FPM CPU > 70% sustained or p95 latency SLO miss |
|
||||
| **be-vpn-2+** | RSSH session count > [§12.1](#121-rssh-remote-access-connections) per node |
|
||||
| **be-media-N** | SFU/ffmpeg SPEC approved |
|
||||
| **CDN edge** | OTA/release origin egress > [§12.3](#123-network-bandwidth-and-egress) budget |
|
||||
| **Regional PoP** | p95 RTT > 300 ms for target markets + media revenue (F1/F3) |
|
||||
| **Managed SQL** | DBA ops > 4 h/month or replica lag requirements |
|
||||
|
||||
### 6.4 Traffic and control planes
|
||||
|
||||
| Plane | Protocols | Termination | Notes |
|
||||
|-------|-----------|-------------|-------|
|
||||
| **Control** | HTTPS :443 | FE → BE :80 | Heartbeats, upload, consoles |
|
||||
| **Git** | HTTPS / SSH | FE path or dedicated | Gitea behind same FE prefix |
|
||||
| **OTA** | HTTPS static | FE → BE or CDN | Large artifacts → object store |
|
||||
| **VPN lab** | UDP :45340 | Router DNAT → FE → BE | Unchanged until WG pool |
|
||||
| **RSSH** | TCP :443 | `ra.apps.f0xx.org` | Bastion may move to dedicated VM |
|
||||
| **Cast LAN** | UDP/TCP :41234–41235 | Device-local | No BE in alpha |
|
||||
| **Cast WAN** | UDP/TURN/WebRTC | SFU tier (future) | See [20260608_BE_SERVICES_and_infra.md](../20260608_BE_SERVICES_and_infra.md) §SFU |
|
||||
|
||||
---
|
||||
|
||||
## 7. Service inventory and scaling profile
|
||||
|
||||
| Service | Stateful? | Scale axis | Co-location rule |
|
||||
|---------|-----------|------------|------------------|
|
||||
| Hub / crashes / tickets | No (PHP) | Horizontal | With API pool |
|
||||
| Graphs ingest + UI | No | Horizontal | Same pool |
|
||||
| Builder UI | No | Horizontal | Same pool; triggers build VM |
|
||||
| Docker build | Ephemeral | Queue workers | **Dedicated** CPU VM |
|
||||
| MariaDB | Yes | Vertical → replica | **Dedicated**; no ffmpeg |
|
||||
| Gitea | Yes (git) | Vertical; mirror RO | Low traffic; ops VM OK |
|
||||
| URL shortener | No | Horizontal | Small; can share ops VM |
|
||||
| OTA artifacts | Yes (files) | CDN / object store | Split from PHP |
|
||||
| WireGuard | Yes (peers) | Pool by region | UDP-friendly VM |
|
||||
| RSSH bastion | Session | Horizontal + sticky | See §12.1 |
|
||||
| SFU / ffmpeg | Session | Horizontal | **Dedicated** media subnet |
|
||||
|
||||
---
|
||||
|
||||
## 8. Deployment and promotion pipeline
|
||||
|
||||
```text
|
||||
feature/* or next ──► dev cluster (auto or manual)
|
||||
│
|
||||
▼ QA + PO
|
||||
staging cluster
|
||||
│
|
||||
▼ soak + sign-off ([ALPHA.md](../ALPHA.md) pattern extended)
|
||||
prod cluster
|
||||
```
|
||||
|
||||
| Step | Actor | Gate |
|
||||
|------|-------|------|
|
||||
| Deploy to **dev** | DEV / CI | Unit tests green |
|
||||
| Promote to **staging** | PO or release delegate | Integration + e2e smoke |
|
||||
| Promote to **prod** | PO | Staging soak; [GIT_FLOW.md](../GIT_FLOW.md) tag on `master` for app releases |
|
||||
|
||||
**Config rule:** `config.php` and secrets are **environment-specific**; post-sync scripts (e.g. `ensure_url_shortener_prod_config.php`) must be idempotent per environment.
|
||||
|
||||
---
|
||||
|
||||
## 9. Zero-downtime and load balancing
|
||||
|
||||
| Tier | Strategy |
|
||||
|------|----------|
|
||||
| **PHP-FPM** | N≥2 VMs behind FE upstream block or cloud LB; `max_fpm_children` tuned per VM |
|
||||
| **MariaDB** | Primary + read replica; app read/write split optional later |
|
||||
| **Gitea** | Single primary; maintenance window for upgrades unless Gitea HA adopted |
|
||||
| **WireGuard** | New peers on least-loaded node; existing peers sticky until session end |
|
||||
| **Static OTA** | Blue bucket + CDN swap or versioned path (`v0/ota/…`) |
|
||||
|
||||
**FE nginx pattern (multi-BE):**
|
||||
|
||||
```nginx
|
||||
upstream androidcast_be {
|
||||
least_conn;
|
||||
server 10.7.16.128:80 max_fails=2 fail_timeout=10s;
|
||||
server 10.7.16.129:80 max_fails=2 fail_timeout=10s;
|
||||
}
|
||||
```
|
||||
|
||||
Cloud providers: prefer **managed LB** (GCP ILB, Azure Front Door/Application Gateway) when FE moves partial traffic to cloud.
|
||||
|
||||
---
|
||||
|
||||
## 10. Data and storage
|
||||
|
||||
| Data class | Store | Backup |
|
||||
|------------|-------|--------|
|
||||
| Relational (crashes, tickets, RA, short links) | MariaDB | Daily logical dump + binlog optional |
|
||||
| Git mirrors | Gitea disk | `gitea_collect_backup.sh`; off-VM copy |
|
||||
| OTA / APK / build artifacts | Object store or large volume | Version immutability |
|
||||
| Cast recordings (future) | Object store + lifecycle policy | Glacier-class after N days |
|
||||
| Audit / graphs time series | MariaDB or TSDB later | DR §open |
|
||||
|
||||
**Rule:** Object store VMs optimize for **disk IOPS and egress**, not CPU.
|
||||
|
||||
---
|
||||
|
||||
## 11. Simulation lab (Alpine HVMs)
|
||||
|
||||
Sysop MAY simulate a cluster on FE hypervisor:
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Base OS | Alpine 3.x (match prod BE) |
|
||||
| Pre-install | `nginx`, `php81`, `php81-fpm`, `mariadb`, `git`, `docker`, `wireguard-tools` |
|
||||
| Network | Private `10.7.0.0/8` — **no WAN topology change** |
|
||||
| Count | 3–7 HVMs typical for dev cluster |
|
||||
|
||||
```text
|
||||
FE XEN
|
||||
├── hvm-be-web-dev
|
||||
├── hvm-be-data-dev
|
||||
├── hvm-be-ops-dev
|
||||
└── hvm-be-build-dev
|
||||
```
|
||||
|
||||
**Deliverable (future script):** `orchestration/sim/cluster-up.sh` — out of scope until PO prioritizes; SPEC reserves name.
|
||||
|
||||
---
|
||||
|
||||
## 12. Capacity planning framework
|
||||
|
||||
### 12.1 RSSH / remote access connections
|
||||
|
||||
Planning numbers for **single bastion VM** (2 vCPU, 2 GiB RAM, Alpine):
|
||||
|
||||
| Metric | Conservative | Target design |
|
||||
|--------|--------------|---------------|
|
||||
| Concurrent **SSH** sessions | 50 | 100 with `MaxSessions` tuning |
|
||||
| Concurrent **heartbeat** devices (poll 1–7 min) | 2 000 | 5 000 (mostly idle HTTP) |
|
||||
| Peak **new sessions / min** | 10 | 30 with queue |
|
||||
| WireGuard **peers** (lab track) | 200 | 500 split across 2 WG nodes |
|
||||
|
||||
**Bottleneck order:** MariaDB connection pool → sshd `MaxStartups` → PHP-FPM for heartbeat API → CPU on bastion.
|
||||
|
||||
When **staging** exceeds conservative column for 24 h, add **be-vpn-2** before cloud migration.
|
||||
|
||||
### 12.2 Cast and media paths (future)
|
||||
|
||||
| Mode | Latency | Scale unit | ffmpeg role |
|
||||
|------|---------|------------|-------------|
|
||||
| LAN P2P | Lowest | N/A (alpha) | None |
|
||||
| SFU relay | Low | 1 SFU ≈ 50–200 viewers (codec dependent) | Optional simulcast |
|
||||
| HLS/m3u8 bridge | Medium–high | 1 worker ≈ 10–30 transcodes | Heavy CPU |
|
||||
|
||||
Detailed SFU sizing deferred to owner SFU SPEC ([OPEN_TASKS_GRAPH.md](../OPEN_TASKS_GRAPH.md)).
|
||||
|
||||
### 12.3 Network bandwidth and egress
|
||||
|
||||
Scale planning MUST include **throughput**, not only CPU/RAM/connections. Hidden bottleneck today: **single FE WAN uplink** → **FE→BE backhaul** → **single BE NIC**.
|
||||
|
||||
#### Link inventory (baseline)
|
||||
|
||||
| Link | Typical role | Metric |
|
||||
|------|--------------|--------|
|
||||
| **Router WAN** (`134.17.26.161`) | Public ingress/egress | Mbps sustained + peak; GB/month |
|
||||
| **FE ↔ BE** (`10.7.0.10` → `10.7.16.128`) | Proxied HTTP(S), internal sync | Mbps; must exceed sum of BE-facing services |
|
||||
| **BE origin** | PHP, MariaDB, Gitea, OTA origin | NIC util %; egress GB/month |
|
||||
| **UDP DNAT** | WireGuard lab | Mbps per active peer × peers |
|
||||
| **Future CDN origin pull** | OTA, recordings, HLS | Origin Mbps; **CDN egress** billed separately |
|
||||
|
||||
#### Per-service bandwidth profile (planning)
|
||||
|
||||
| Service | Direction | Order of magnitude (prod planning) | Scale trigger |
|
||||
|---------|-----------|-----------------------------------|---------------|
|
||||
| Control APIs (upload, heartbeat) | In ≪ out | 1–20 Mbps peak; low GB/month | p95 latency, not Mbps |
|
||||
| Consoles / hub | Out | < 5 Mbps sustained | CDN cache static assets |
|
||||
| OTA APK / `.otapkg` | Out | **Burst 50–500 Mbps** during release | CDN **required** before wide rollout |
|
||||
| Gitea clone/fetch | Out | 10–100 Mbps burst | Mirror geo cache |
|
||||
| URL shortener redirect | Out | Low | — |
|
||||
| Cast recordings (F3) | Out | **100 Mbps–1 Gbps** per hot object | Object store + CDN; geo replicas |
|
||||
| SFU relay (F1) | In+out symmetric | **5–50 Mbps × streams** | Dedicated media NIC / region |
|
||||
| ffmpeg transcode (F2) | In+out | **10–100 Mbps** per job | Worker pool + queue depth |
|
||||
| Commercial VPN (F4) | UDP bidirectional | **1–5 Mbps × active tunnels** | WG pool per region |
|
||||
|
||||
#### Budget template (PO fills per environment)
|
||||
|
||||
| Metric | Dev | Staging | Prod (initial) | Alert |
|
||||
|--------|-----|---------|----------------|-------|
|
||||
| WAN egress GB/month | — | — | _TBD_ | 80% of ISP/cloud quota |
|
||||
| Peak WAN Mbps | — | — | _TBD_ | 70% of measured cap |
|
||||
| FE→BE peak Mbps | — | — | _TBD_ | sustained > 60% 15 min |
|
||||
| OTA release peak | — | soak test | CDN absorbs > 90% | origin > 100 Mbps |
|
||||
| Media egress GB/month | — | — | _TBD_ (F3+) | cost budget |
|
||||
|
||||
**DR rule:** Do not add **be-web-2** for bandwidth until CDN/OTA offload is evaluated — horizontal PHP nodes do not fix a saturated WAN link.
|
||||
|
||||
---
|
||||
|
||||
## 13. Cloud migration (GCP / Azure / other)
|
||||
|
||||
**Hybrid recommended** for first cloud phase:
|
||||
|
||||
| Stay on-prem (FE + latency-sensitive) | Cloud candidate |
|
||||
|---------------------------------------|-----------------|
|
||||
| Gentoo FE TLS (or parallel cloud LB) | Object store (GCS/Azure Blob) |
|
||||
| WireGuard UDP if DNAT chain kept | Managed MariaDB |
|
||||
| LAN cast | ffmpeg worker MIG / VMSS |
|
||||
| | SFU pool (GPU optional) |
|
||||
|
||||
**Migration sequence (normative):**
|
||||
|
||||
1. Object store for OTA + build artifacts
|
||||
2. Read-only analytics replica or backup target
|
||||
3. Staging cluster entirely in cloud (dress rehearsal)
|
||||
4. Prod PHP tier behind hybrid LB
|
||||
5. Media plane last (highest risk)
|
||||
|
||||
**Egress rule:** Keep **one** canonical public URL per service; DNS/CNAME moves — no app hard-coded IP changes.
|
||||
|
||||
**Bandwidth rule:** Model **cloud egress pricing** in phase 1 object-store migration; prefer **CDN in front of origin** for any artifact > 1 MB served to > 100 users.
|
||||
|
||||
---
|
||||
|
||||
## 14. Global availability and GEO policy
|
||||
|
||||
Draft intent: **anyone in the world can use the platform** (latency permitting), while **specific content or features may be excluded by region** — e.g. a recording or OTA channel must **not** be published or downloadable in **Zambia (`ZM`)** when PO sets that policy.
|
||||
|
||||
### 14.1 Reach model
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph Users["Global users"]
|
||||
U1["Americas"]
|
||||
U2["Europe / Africa"]
|
||||
U3["Asia-Pacific"]
|
||||
end
|
||||
|
||||
subgraph Edge["Edge layer"]
|
||||
DNS["GeoDNS / Anycast DNS"]
|
||||
CDN["CDN + WAF geo rules"]
|
||||
FE0["Primary FE TLS — on-prem"]
|
||||
end
|
||||
|
||||
subgraph Origin["Origin cluster(s)"]
|
||||
BE["BE VMs / cloud origin"]
|
||||
OBJ["Object store"]
|
||||
MEDIA["SFU / media PoP future"]
|
||||
end
|
||||
|
||||
U1 --> DNS
|
||||
U2 --> DNS
|
||||
U3 --> DNS
|
||||
DNS --> CDN
|
||||
CDN --> FE0
|
||||
CDN --> OBJ
|
||||
FE0 --> BE
|
||||
BE --> OBJ
|
||||
MEDIA --> OBJ
|
||||
```
|
||||
|
||||
| Phase | Reach | Latency target (control plane) |
|
||||
|-------|-------|--------------------------------|
|
||||
| **Now** | Single FE/BE (EU-centric) | Best-effort worldwide HTTPS |
|
||||
| **Phase 5–6** | CDN for static + OTA | p95 < 300 ms TTFB via edge |
|
||||
| **Future** | Optional regional **media PoP** + signaling | RT cast < 150 ms LAN-equivalent where PoP exists |
|
||||
|
||||
**Normative:** Public APIs and consoles remain **globally reachable** unless PO declares a **maintenance** or **sanctions** block at edge (separate from per-content deny).
|
||||
|
||||
### 14.2 Segment isolation
|
||||
|
||||
| Concept | Definition |
|
||||
|---------|------------|
|
||||
| **Segment** | ISO country code, optional region group (`EU`, `AFRICA`, custom list) |
|
||||
| **Default** | **Allow serve** worldwide for control plane; content inherits tenant default |
|
||||
| **deny_publish** | Content must not be **made available** in listed segments (upload/mirror/index) |
|
||||
| **deny_serve** | Existing object must not be **delivered** (HTTP 451 / empty manifest / VPN reject) |
|
||||
| **allow_only** | Exclusive list — only these segments (stricter catalogues) |
|
||||
|
||||
**Example (illustrative):**
|
||||
|
||||
```json
|
||||
{
|
||||
"resource": "cast_recording:abc123",
|
||||
"geo_policy": {
|
||||
"default": "allow",
|
||||
"deny_serve": ["ZM"],
|
||||
"deny_publish": ["ZM"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Same pattern applies to OTA channels, short-link targets (optional), and paid stream entitlements (F5).
|
||||
|
||||
| Content class | Typical policy |
|
||||
|---------------|----------------|
|
||||
| Crash/ticket consoles | Global (operator VPN/RBAC unchanged) |
|
||||
| OTA `stable` channel | Global unless PO restricts beta regions |
|
||||
| Cast recording (F3) | **Per-object** deny segments |
|
||||
| HLS rebroadcast (F9) | Per-channel geo + copyright rules |
|
||||
| Commercial VPN exit (F4) | **egress region** choice, not arbitrary bypass of deny_serve |
|
||||
|
||||
### 14.3 Enforcement layers
|
||||
|
||||
| Layer | Mechanism | When |
|
||||
|-------|-----------|------|
|
||||
| **L1 DNS / GeoDNS** | Route to nearest healthy edge | Multi-PoP phase |
|
||||
| **L2 CDN / WAF** | Geo block or cache vary-by-country | OTA, static, recording download |
|
||||
| **L3 FE nginx** | `geo` module or auth subrequest | Optional coarse blocks |
|
||||
| **L4 BE API** | Resolve client country (CDN header `CF-IPCountry`, GeoIP) → **geo_policy** check | Normative for dynamic content |
|
||||
| **L5 Object store** | Bucket policy / signed URL with segment claim | Large artifacts |
|
||||
| **L6 App / entitlements** | Mobile checks BE before playback | F3, F5, F9 |
|
||||
|
||||
**Headers (normative when behind CDN):** trust `X-Geo-Country` or provider equivalent only from **FE/CDN** — strip at edge from client.
|
||||
|
||||
**Audit:** deny_serve events logged to audit table (who, resource, country, time) for operator review.
|
||||
|
||||
**Dev/staging:** may use `X-Debug-Country: ZM` header to simulate segments without VPN.
|
||||
|
||||
---
|
||||
|
||||
## 15. RBAC and operator model evolution
|
||||
|
||||
| Phase | Model |
|
||||
|-------|-------|
|
||||
| **Now** | Developer-centric; shared `ac_crash_sess`; privilege sets for tickets, RA, short links |
|
||||
| **Staging scale** | Separate **operator** vs **developer** orgs in Gitea; audit log export |
|
||||
| **Prod commercial** | Tenant-scoped RBAC; customer admin roles; SSO optional |
|
||||
|
||||
Platform MUST support **per-environment** user tables or row-level `environment_id` before multi-tenant commercial features (F5–F9).
|
||||
|
||||
---
|
||||
|
||||
## 16. Commercial and paid services (platform hooks)
|
||||
|
||||
No implementation in this SPEC — only **interfaces**:
|
||||
|
||||
| Hook | Requirement |
|
||||
|------|-------------|
|
||||
| **Billing** | Webhook idempotency; `tenant_id` on all paid resources |
|
||||
| **Metering** | VPN minutes, storage GB, transcode minutes, **egress GB** — export to metrics |
|
||||
| **Entitlements** | Mobile app checks BE `/api/v1/entitlements` (future); includes **geo_allowed** flag |
|
||||
| **Add-ons** | Feature flags per user; GPU queue for masks/background |
|
||||
|
||||
Payment provider SHOULD have **no monthly minimum** at low volume (draft: “near 0 for developer”).
|
||||
|
||||
---
|
||||
|
||||
## 17. Observability and SLO
|
||||
|
||||
| Service | Health | SLO (prod, initial) |
|
||||
|---------|--------|---------------------|
|
||||
| Control APIs | `/api/…` + upload smoke | 99.5% monthly |
|
||||
| Consoles | HTTP 200 login page | 99.5% |
|
||||
| URL shortener | `/api/v1/health` | 99.9% |
|
||||
| RSSH heartbeat | device poll success rate | 99% |
|
||||
| Gitea | mirror sync age < 2× interval | Best effort |
|
||||
|
||||
**Bandwidth metrics (add to graphs / future central metrics):**
|
||||
|
||||
| Metric | Source | Alert |
|
||||
|--------|--------|-------|
|
||||
| `wan_egress_mbps` | router or FE interface | > 70% cap 15 min |
|
||||
| `fe_be_backhaul_mbps` | FE→BE link | > 60% cap |
|
||||
| `origin_egress_gbytes_month` | nginx / object store | > 80% budget |
|
||||
| `cdn_cache_hit_ratio` | CDN analytics | < 85% during OTA |
|
||||
| `geo_deny_serve_total` | BE audit | anomaly spike |
|
||||
| `sfu_media_mbps` | SFU (future) | per-PoP capacity plan |
|
||||
|
||||
Use existing graphs console + future centralized metrics ([GRAFANA_vs_others_graphvis_pivot.md](../GRAFANA_vs_others_graphvis_pivot.md)).
|
||||
|
||||
---
|
||||
|
||||
## 18. Safety constraints (current topology)
|
||||
|
||||
From [bottomline_reminder.txt](../../bottomline_reminder.txt) and [INFRA.md](../INFRA.md):
|
||||
|
||||
| Rule | Detail |
|
||||
|------|--------|
|
||||
| **No risky WAN changes** | Router `134.17.26.161` → FE `10.7.0.10` chain stays until DR-approved migration |
|
||||
| **Port 8089** | Janus/other — not androidcast vhost |
|
||||
| **BE listen 80** | Canonical for FE `proxy_pass` |
|
||||
| **ssh access** | `ssh alpine-be`, `ssh f0xx-monstro`, `ssh f0xx.org` — inspect `tmp/FE_gentoo`, `tmp/BE_alpine` before infra edits |
|
||||
| **Simulation only** | New HVMs on intra `10.7.0.0/8`; no public IP experiments without PO |
|
||||
|
||||
---
|
||||
|
||||
## 19. Phased delivery map
|
||||
|
||||
| Phase | Scope | Alpha relation |
|
||||
|-------|-------|----------------|
|
||||
| **0** | Document + simulation design (this SPEC/DR) | Parallel to alpha |
|
||||
| **1** | Split **build** Docker to dedicated VM; OTA on object disk | Post-alpha |
|
||||
| **2** | **be-web-2** + FE upstream; staging cluster on HVMs | Post-alpha |
|
||||
| **3** | Managed backup; MariaDB replica | Post-alpha |
|
||||
| **4** | SFU + ffmpeg workers (separate SPEC) | Post-alpha |
|
||||
| **5** | Hybrid cloud staging + **CDN for OTA/static** | Strategic |
|
||||
| **5b** | **Bandwidth dashboards** + egress budgets on graphs | Strategic |
|
||||
| **6** | Commercial hooks F5–F9 | Product-dependent |
|
||||
| **7** | **GEO policy** enforcement (F10) for recordings/OTA/catalog | After F3 or PO mandate |
|
||||
|
||||
---
|
||||
|
||||
## 20. Out of scope (this SPEC revision)
|
||||
|
||||
- Concrete Terraform/Pulumi modules
|
||||
- Pricing quotes for GCP/Azure
|
||||
- Legal/compliance for payments and VPN resale
|
||||
- Mobile app changes for commercial features
|
||||
- Replacing MariaDB with Postgres
|
||||
- Sanctions-law mapping per country — PO/legal owns lists; platform stores **deny segments** only
|
||||
|
||||
---
|
||||
|
||||
## 21. Related docs
|
||||
|
||||
| Doc | Link |
|
||||
|-----|------|
|
||||
| Infra topology | [INFRA.md](../INFRA.md) |
|
||||
| BE service map | [20260608_BE_SERVICES_and_infra.md](../20260608_BE_SERVICES_and_infra.md) |
|
||||
| Build / Docker | [BUILD_DEPLOY.md](../BUILD_DEPLOY.md) |
|
||||
| Remote access | [REMOTE_ACCESS_IMPL.md](../REMOTE_ACCESS_IMPL.md), [20260602_REVERSE_SSH_proposals_summary.md](../20260602_REVERSE_SSH_proposals_summary.md) |
|
||||
| Open tasks graph | [OPEN_TASKS_GRAPH.md](../OPEN_TASKS_GRAPH.md) |
|
||||
| Alpha priorities | [20260608_ALPHA_PRIORITIES.md](../20260608_ALPHA_PRIORITIES.md) |
|
||||
| URL shortener | [specs/20100611_3_url_shortener.md](20100611_3_url_shortener.md) |
|
||||
| Design review | [DRs/20100612_1_scaling.md](../DRs/20100612_1_scaling.md) |
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
| Date | Change |
|
||||
|------|--------|
|
||||
| 2026-06-12 | Initial SPEC from draft `20100612_1_scaling.txt` |
|
||||
| 2026-06-12 | Rev. 2 — network bandwidth (§12.3), global availability + GEO policy (§14), metrics |
|
||||
817
docs/specs/20100612_1_scaling.pdf
Normal file
817
docs/specs/20100612_1_scaling.pdf
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user