+
    jHàjý  ã                  ó2  a € 0 t $ R t^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t	^ RI
HtHt ^ RIHt ^ RIHt ^ RIHtHtHt ]P*                  ! ]4      tRtRtR	t] ! R
 R4      4       tR R ltR R ltR R lt] ! R R4      4       t]P@                  R R l4       t!R R lt"R R lt#R R lt$R t%R R lt&R R  lt'R! R" lt(R# R$ lt)R% R& lt*R'R/R( R) llt+R* R+ lt,R, R- lt-R. R/ lt.R0 R1 lt/R2 R3 lt0R4 R5 lt1]P@                  R6 R7 l4       t2R8 R9 lt3R: R; lt4R< R= lt5R> R? lt6R@ RA lt7RB RC lt8RD RE lt9RF RG lt:RH RI lt;]8];3t<RJ]=RK&   RL RM lt>RN RO lt?RP RQ lt@RR RS ltART RU ltBRV RW ltCRX RY ltDRZ R[ ltER\ R] ltFR^ R_ ltGR` Ra ltHRbtIRc Rd ltJRe Rf ltKRg Rh ltLRi Rj ltMRk Rl ltNRm Rn ltORo Rp ltPR'R/Rq Rr lltQRs Rt ltRRu Rv ltSRw Rx ltTRy Rz ltUR{ R| ltVR}]/R~ R lltWR€tXR� R‚ ltYR‹Rƒ R„ lltZR… R† lt[R‡ Rˆ lt\R‰ RŠ lt]R# )ŒuÐ  ``hermes gateway migrate --multiplex``: converge a per-profile-gateway install onto the ONE
host gateway, with a table-driven preflight.

Multiplex-only (Teknium ruling): exactly one ``hermes gateway run`` per host, serving every
profile. This command is the supported convergence path, and it is defined by TOPOLOGY, not by a
config flag â€” a host is converged when no secondary profile owns a gateway process or a supervisor
unit any more. That makes it re-runnable: a half-migrated host (flag flipped, a unit left behind, a
crash between the two) converges on the next run instead of being reported "already multiplexed".

There is no ``--standalone`` rollback command: reinstalling per-profile services is no longer a
supported topology. The rollback machinery survives as the COMPENSATOR inside a single failed
apply (:func:`rollback_migration`) â€” which restores exactly ONE gateway, the default's, because
the host gateway lock now refuses the fleet it used to rebuild â€” and the recorded manifest is what
the next re-run resumes from. A manifest on disk outranks every preflight gate: a resume
compensates an already-destructive state instead of initiating one.

The preflight reuses the gateway's own conflict logic (``GatewayRunner._adapter_credential_fingerprint``,
``platform_binds_port``, the adapters' ``serves_profile_prefix`` declaration) so its verdict matches
what the multiplexer would do at startup. ``hermes update`` calls :func:`maybe_auto_migrate_after_update`.
)ÚannotationsN)Ú	dataclassÚfield)ÚPath)ÚSimpleNamespace)ÚCallableÚIteratorÚOptionalzgateway_migration.jsonz"hermes gateway migrate --multiplexg     €V@c                  óô   € ] tR t^-t$ RtR]R&   R]R&   RtR]R&   ]! ]R	7      t	R
]R&   Rt
R]R&   RtR]R&   RtR]R&   ]R R l4       t]R R l4       t]R R l4       t]R R l4       tR R ltR R ltRtR# )ÚProfileGatewayzNOne profile's standalone gateway footprint: live PID and/or installed service.ÚstrÚnamer   ÚhomeNúOptional[int]Úpid©Údefault_factoryúlist[tuple[str, bool]]ÚservicesúOptional[str]Úrun_as_userÚuidúOptional[Path]Úruntime_homec               ó   € V ^8„  d   QhRR/# ©é   ÚreturnÚbool© )Úformats   "Ú?/home/ubuntu/.hermes/hermes-agent/hermes_cli/gateway_migrate.pyÚ__annotate__ÚProfileGateway.__annotate__;   s   € ÷ &ñ &˜Dñ &ó    c                	ó    € V P                   R 8H  # )Údefault)r   ©Úselfs   &r!   Ú
is_defaultÚProfileGateway.is_default:   s   € à�y‰y˜IÑ%Ð%r$   c               ó   € V ^8„  d   QhRR/# ©r   r   úOptional[tuple[str, bool]]r   )r    s   "r!   r"   r#   ?   s   € ÷ ;ñ ;Ð3ñ ;r$   c                óP   € V P                   '       d   V P                   ^ ,          # R# )zTThe unit the default gateway is (re)started through; None when nothing is installed.N©r   r'   s   &r!   ÚserviceÚProfileGateway.service>   s    € ð $(§=§= =ˆt�}‰}˜QÕÐ:°dÐ:r$   c               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   r#   D   s   € ÷ ;ñ ;˜Tñ ;r$   c                	óX   € V P                   R J;'       g    \        V P                  4      # ©N)r   r   r   r'   s   &r!   Úhas_gatewayÚProfileGateway.has_gatewayC   s"   € à�x‰x˜tÐ#×:Ð:¤t¨D¯M©MÓ':Ð:r$   c               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   r#   H   s   € ÷ 2ñ 2 ñ 2r$   c                	ó    € RV P                   9   # )Úsystemd©r9   Tr/   r'   s   &r!   Úhas_system_unitÚProfileGateway.has_system_unitG   s   € à  D§M¡MÑ1Ð1r$   c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r    s   "r!   r"   r#   K   s   € ÷ añ a˜sñ ar$   c                	ón   € V P                   '       d#   R P                  R V P                    4       4      # R# )ú + c              3  ó8   "  € T F  p\        V4      x € K  	  R # 5ir4   )Ú_service_label©Ú.0Úss   & r!   Ú	<genexpr>Ú/ProfileGateway.service_label.<locals>.<genexpr>L   s   é € ÐC±]°œ.¨×+Ð+³]ùó   ‚Únone)r   Újoinr'   s   &r!   Úservice_labelÚProfileGateway.service_labelK   s)   € ØGKÇ}Ç}À}ˆu�z‰zÑC°T·]²]ÓCÓCÐ`ÐZ`Ð`r$   c               ó   € V ^8„  d   QhRR/# ©r   r   Údictr   )r    s   "r!   r"   r#   N   s   € ÷ 
ñ 
˜ñ 
r$   c                	óz  € R V P                   R\        V P                  4      RV P                  RV P                  f   RM\        V P                  4      RV P                   Uu. uF  p\        V4      NK  	  upRV P                  RV P                  RV P                  f   R/# \        V P                  4      /# u upi )	Úprofiler   r   r0   Nr   r   r   r   )
r   r   r   r   r0   Ú_service_dictr   r   r   r   )r(   rD   s   & r!   Úto_dictÚProfileGateway.to_dictN   s¥   € à�t—y‘y &¬#¨d¯i©i«.¸%ÀÇÁØ˜tŸ|™|Ò3‘t¼ÀtÇ|Á|Ó9TØ°4·=²=ÓA±=¨aœ qÖ)±=ÑAØ˜4×+Ñ+Ø�4—8‘8˜^°T×5FÑ5FÒ5N¨Tð
ð 	
ô
 UXÐX\×XiÑXiÓTjð
ð 	
ùò Bs   Á"B8r   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__annotations__r   r   Úlistr   r   r   r   Úpropertyr)   r0   r5   r;   rJ   rR   Ú__static_attributes__r   r$   r!   r   r   -   s§   ‡ áXØ
ƒIØ
ƒJØ€CˆÓñ (-¸TÔ'B€HÐ$ÓBØ!%€K�Ó%Ø€CˆÓØ#'€L�.Ó'àô&ó ð&ð ô;ó ð;ð ô;ó ð;ð ô2ó ð2õa÷
ñ 
r$   r   c               ó    € V ^8„  d   QhRRRR/# ©r   r0   útuple[str, bool]r   r   r   )r    s   "r!   r"   r"   X   s   € ÷ Cñ CÐ,ð C°ñ Cr$   c                óf   € V w  rVR 8X  d   RV'       d   R R2# R R2# VR8X  d   R# VR8X  d   R# T# )	r9   z	systemd (ÚsystemÚuserÚ)Ús6zs6 slotÚwindowszWindows scheduled taskr   )r0   Úkindra   s   &  r!   rA   rA   X   sM   € Ø�L€DØˆyÔØ§v˜8Ð:¸!Ð<Ð<°6Ð:¸!Ð<Ð<Øˆt„|ÙØ'+¨yÔ'8Ð#ÐB¸dÐBr$   c               ó    € V ^8„  d   QhRRRR/# r^   r   )r    s   "r!   r"   r"   a   s   € ÷ 9ñ 9Ð*ð 9¨sñ 9r$   c                ó&   € V ^ ,          R8X  d   R# R# )z{An s6 slot is parked down (it stays registered as the `hermes -p X gateway start` target), every
other unit is uninstalled.rd   ÚparkÚ	uninstallr   ©r0   s   &r!   Ú_remove_verbrl   a   s   € ð ˜Q•Z 4Ô'ˆ6Ð8¨[Ð8r$   c               ó    € V ^8„  d   QhRRRR/# )r   r0   r_   r   rN   r   )r    s   "r!   r"   r"   g   s   € ÷ 6ñ 6Ð+ð 6°ñ 6r$   c                ó*   € R V ^ ,          RV ^,          /# )rf   ra   r   rk   s   &r!   rQ   rQ   g   s   € Ø�G˜A•J ¨'°!­*Ð5Ð5r$   c                  óV  € ] tR t^kt$ R]R&   R]R&   R]R&   R]R&   R	tR
]R&   RtR]R&   ]! ]R7      t	R]R&   ]! ]R7      t
R]R&   R(tR]R&   ]R R l4       t]R R l4       t]R R l4       t]R R l4       t]R R l4       t]R R l4       tR  R! ltR" R# ltR$ R% ltR& R' ltR(tR	# ))ÚMigrationPlanr   Údefault_homeúlist[ProfileGateway]Úprofilesr   Úmultiplex_flag_onúOptional[list[str]]Úlive_servedNúOptional[dict]ÚmanifestFÚinterruptedr   ú	list[str]ÚblockersÚnoticesztuple[str, ...]Ústandalone_by_configc               ó   € V ^8„  d   QhRR/# ©r   r   rr   r   )r    s   "r!   r"   ÚMigrationPlan.__annotate__‚   s   € ÷ >ñ >Ð1ñ >r$   c                	ój   € V P                    Uu. uF  qP                  '       d   K  VNK  	  up# u upi r4   )rs   r)   ©r(   Úps   & r!   ÚsecondariesÚMigrationPlan.secondaries�   s$   € àŸ=š=Ó=™=�a·µ—�™=Ñ=Ð=ùÒ=ó   �0§0c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r    s   "r!   r"   r€   †   s   € ÷ >ñ >˜ñ >r$   c                	ó:   € \        R  V P                   4       4      # )c              3  óL   "  € T F  qP                   '       g   K  Vx € K  	  R # 5ir4   )r)   ©rC   rƒ   s   & r!   rE   Ú(MigrationPlan.default.<locals>.<genexpr>‡   s   é € Ð=™}˜!·µ—A’A›}ùs   ‚$š
$)Únextrs   r'   s   &r!   r&   ÚMigrationPlan.default…   s   € äÑ=˜tŸ}š}Ó=Ó=Ð=r$   c               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   r€   Š   s   € ÷ 	^ñ 	^ Tñ 	^r$   c                óà   € V P                   '       g   V P                  '       d   R# V P                  ;'       g6    \        V P                  ;'       d    \        V P                  4      ^8„  4      # )a-  Converged: nothing is left for this command to do.

TOPOLOGY, not the flag. A host whose flag is on while a secondary still owns a gateway
process or a supervisor unit is HALF-migrated; reporting that as "already multiplexed"
made the re-run a no-op on exactly the host that needed it most (#100896).
F)ry   Ústandalone_secondariesrt   r   rv   Úlenr'   s   &r!   Úalready_multiplexedÚ!MigrationPlan.already_multiplexed‰   sV   € ð ××Ð˜t×:×:Ð:ÙØ×%Ñ%×]Ð]¬¨d×.>Ñ.>×.\Ð.\Ä3Àt×GWÑGWÓCXÐ[\ÑC\Ó)]Ð]r$   c               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   r€   –   s   € ÷ >ñ >Ð(<ñ >r$   c                	ój   € V P                    Uu. uF  qP                  '       g   K  VNK  	  up# u upi r4   )r„   r5   r‚   s   & r!   r�   Ú$MigrationPlan.standalone_secondaries•   s(   € à×+Ò+Ó=Ñ+�a¯}­}—�Ñ+Ñ=Ð=ùÒ=r†   c               ó   € V ^8„  d   QhRR/# )r   r   úset[str]r   )r    s   "r!   r"   r€   š   s   € ÷ `ñ ` xñ `r$   c                	óº   € ^ RI Hp V P                   Uu0 uF:  q"P                  '       g   V! VP                  4      '       d   K.  VP
                  kK<  	  up# u upi )é    ©Úprofile_is_parked)Úhermes_cli.profilesrœ   rs   r)   r   r   )r(   rœ   rƒ   s   &  r!   Úexpected_served_namesÚ#MigrationPlan.expected_served_names™   s<   € å9Ø $§¢Ó_¡˜1··°ÑEVÐWX×W]ÑW]×E^”�—”¡Ñ_Ð_ùÒ_s   •+AÁAc               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   r€   Ÿ   s   € ÷ #ñ #˜ñ #r$   c                	ó,   € \        V P                  4      # r4   )r   r{   r'   s   &r!   ÚblockedÚMigrationPlan.blockedž   s   € ä�D—M‘MÓ"Ð"r$   c               ó   € V ^8„  d   QhRR/# r,   r   )r    s   "r!   r"   r€   ¢   s   € ÷ Zñ ZÐ%?ñ Zr$   c                ó˜   € V P                   P                  e   V P                   P                  # \        R V P                   4       R4      # )z”Service manager the default gateway should end up on: its own, else the one the
secondaries used (so a systemd-managed fleet stays systemd-managed).Nc              3  óX   "  € T F   qP                   f   K  VP                   x € K"  	  R # 5ir4   rk   rŠ   s   & r!   rE   Ú4MigrationPlan.target_service_kind.<locals>.<genexpr>§   s   é € ÐRÑ(8 1¿I¹I”Y�Q—Y–YÓ(8ùs   ‚*–*)r&   r0   rŒ   r„   r'   s   &r!   Útarget_service_kindÚ!MigrationPlan.target_service_kind¢   s>   € ð �<‰<×ÑÒ+Ø—<‘<×'Ñ'Ð'ÜÑR¨×(8Ò(8ÓRÐTXÓYÐYr$   c               ó   € V ^8„  d   QhRR/# ©r   r   r   r   )r    s   "r!   r"   r€   ©   s   € ÷ Vñ V Mñ Vr$   c                ó    € V P                   P                  '       d   V P                   P                  # \        R V P                   4       R4      # )zAPreserve the system unit identity that the default unit replaces.c              3  ó`   "  € T F$  qP                   '       g   K  VP                   x € K&  	  R # 5ir4   ©r   rŠ   s   & r!   rE   Ú3MigrationPlan.target_run_as_user.<locals>.<genexpr>­   s   é € ÐNÑ,< qÇÅ”]�Q—]–]Ó,<ùs   ‚.š.N)r&   r   rŒ   r„   r'   s   &r!   Útarget_run_as_userÚ MigrationPlan.target_run_as_user©   s<   € à�<‰<×#×#Ð#Ø—<‘<×+Ñ+Ð+ÜÑN¨D×,<Ò,<ÓNÐPTÓUÐUr$   c               ó   € V ^8„  d   QhRR/# rM   r   )r    s   "r!   r"   r€   ¯   s   € ÷ 
ñ 
˜ñ 
r$   c                	ó–  € R \        V P                  4      RV P                   Uu. uF  qP                  4       NK  	  upR\	        V P
                  4      RV P                  RV P                  RV P                  RV P                  R\	        V P                  4      R\	        V P                  4      R	V P                  4       R
\        /# u upi )rq   rs   r}   rt   rv   r’   ry   r{   r|   ÚeligibleÚcommand)r   rq   rs   rR   rZ   r}   rt   rv   r’   ry   r{   r|   Úeligible_for_migrationÚMIGRATE_COMMANDr‚   s   & r!   rR   ÚMigrationPlan.to_dict¯   s¦   € àœC × 1Ñ 1Ó2Ø¨d¯mªmÓ<©m¨Ÿ™ž©mÑ<Ø"¤D¨×)BÑ)BÓ$CØ ×!7Ñ!7Ø˜4×+Ñ+Ø! 4×#;Ñ#;Ø˜4×+Ñ+Øœ˜TŸ]™]Ó+Ø”t˜DŸL™LÓ)Ø˜×3Ñ3Ó5Ø”ð
ð 	
ùâ<s   ¥C
c               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   r€   ¾   s   € ÷ 
ñ 
¨ñ 
r$   c                óÐ   € \        V P                  4      ^8¬  ;'       dH    \        V P                  4      ;'       d+    V P                  '       * ;'       d    V P
                  '       * # )zê>= 2 profiles, at least one secondary with its own gateway, multiplex off, no blockers.
This is the AUTO-migration (``hermes update``) bar; the explicit command also proceeds with
zero standalone secondaries (see :func:`cmd_migrate`).)r‘   rs   r   r�   r’   r¢   r'   s   &r!   r¶   Ú$MigrationPlan.eligible_for_migration¾   sa   € ô
 �—‘Ó !Ñ#÷ Bð B¬¨T×-HÑ-HÓ(I÷ Bð BØ×,Ñ,Ô,÷Bð BØ59·\±\Ô1Að	
r$   r   )rT   rU   rV   rW   rY   rx   ry   r   rZ   r{   r|   r}   r[   r„   r&   r’   r�   rž   r¢   r¨   r°   rR   r¶   r\   r   r$   r!   rp   rp   k   sò   ‡ àÓØ"Ó"ØÓØ$Ó$ð
  $€HˆnÓ#ð
 €K�ÓÙ°Ô5€HˆiÓ5Ù¨tÔ4€GˆYÓ4ð -/Ð˜/Ó.àô>ó ð>ð ô>ó ð>ð ô	^ó ð	^ð ô>ó ð>ð ô`ó ð`ð ô#ó ð#õZõVõ
÷
ñ 
r$   rp   c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   úIterator[None]r   )r    s   "r!   r"   r"   Ì   s   € ÷  :ñ  :�Dð  :˜^ñ  :r$   c              #  ó˜  "  € ^ RI HpHpHpHpHp ^ RI p\        P                  P                  R4      pV! 4       '       * pV'       d   V! V! 4       4       V! \        V 4      4      p	\        V 4      \        P                  R&   RVn
         Rx € V'       d	   V! R4       V! V	4       Vf"   \        P                  P                  RR4       MV\        P                  R&   RVn
        R#   T'       d	   T! R4       T! T	4       Tf)   \        P                  P                  RR4       RTn
        i T\        P                  R&   RTn
        i ; i5i)ut  Run service-manager helpers as if ``home`` were the active HERMES_HOME. Both the contextvar
override (``get_hermes_home``) and ``os.environ`` (``gateway.status`` identity files, unit
generation) are switched, then restored.

The process keeps its LAUNCH identity while swapped (``pin_process_hermes_home``): ``os.environ``
still holds the launch profile's ``.env``, so a child spawned in here for ``home`` must see it as
a routed home and take only ``home``'s own secrets â€” with the env var swapped, the routed-home
checks read ``home`` as the launch profile and handed the launch profile's credentials to the
default gateway.)Úget_routing_process_hermes_homeÚpin_process_hermes_homeÚprocess_hermes_home_is_pinnedÚreset_hermes_home_overrideÚset_hermes_home_overrideNÚHERMES_HOME)Úhermes_constantsr¿   rÀ   rÁ   rÂ   rÃ   ÚosÚenvironÚgetr   Ú_default_hermes_root_memoÚpop)
r   r¿   rÀ   rÁ   rÂ   rÃ   rÅ   ÚpreviousÚpinned_hereÚtokens
   &         r!   Ú	_home_envrÎ   Ë   s  é € ÷õ ó Ü�z‰z�~‰~˜mÓ,€HÙ3Ó5Ô5€KßÙÑ ?Ó AÔBÙ$¤S¨£YÓ/€EÜ # D£	„B‡J�Jˆ}ÑØ15ÐÔ.ð
:ÛçÙ# DÔ)Ù" 5Ô)ØÒÜ�J‰J�N‰N˜=¨$Õ/à(0ŒB�J‰J�}Ñ%Ø59ÐÖ2ø÷ Ù# DÔ)Ù" 5Ô)ØÒÜ�J‰J�N‰N˜=¨$Ô/ð 6:ÐÕ2ð )1ŒB�J‰J�}Ñ%Ø59ÐÕ2üs    ‚BE
Â
C' ÂAE
Ã'A EÅE
c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r    s   "r!   r"   r"   ï   s   € ÷ %ñ %�tñ %r$   c                 ó   € ^ RI Hp  V ! 4       # )rš   ©Úget_default_hermes_root)rÅ   rÒ   rÑ   s    r!   Ú_default_homerÓ   ï   s   € Ý8Ù"Ó$Ð$r$   c               ó   € V ^8„  d   QhRR/# )r   r   zlist[tuple[str, Path]]r   )r    s   "r!   r"   r"   ô   s   € ÷ Hñ HÐ.ñ Hr$   c                 ó4   € ^ RI Hp  \        V ! RRR7      4      # )rš   ©Úprofiles_to_serveT)Ú	multiplexÚinclude_parked)r�   r×   rZ   rÖ   s    r!   Ú_profile_homesrÚ   ô   s   € Ý5ÜÑ!¨DÀÔFÓGÐGr$   c               ó    € V ^8„  d   QhRRRR/# ©r   r   r   r   r   r   )r    s   "r!   r"   r"   ù   s   € ÷ ñ ˜Dð  ]ñ r$   c                ó    € ^ RI Hp \        P                  ! \        4      ;_uu_ 4        V! V 4      uuRRR4       #   + '       g   i     R# ; i)a²  Verified PID of a gateway SERVING ``home``, else None (never raises: a probe failure must
not abort a migration plan).

Topology REPORTING, not ownership: ``live_gateway_pid_for_home`` deliberately answers with the
host multiplexer's PID for every home it serves (``gateway.status._host_gateway_serves_home``),
so "this profile is being served" reads as a live PID. Use :func:`_own_gateway_pid` for the
question this command acts on.
)Úlive_gateway_pid_for_homeN)Úgateway.statusrÞ   Ú
contextlibÚsuppressÚ	Exception)r   rÞ   s   & r!   Ú_live_gateway_pidrã   ù   s2   € õ 9Ü	×	Ò	œY×	'Õ	'Ù(¨Ó.÷ 
(×	'×	'Ñ	'áús	   ©<¼A	c                 óž   € \         P                  ! \        4      ;_uu_ 4        ^ RIHp  V ! 4       uuRRR4       #   + '       g   i     R# ; i)zVThe ONE live host gateway (``gateway.host_attach.HostGateway``) or None. Never raises.©Úhost_gatewayN)rà   rá   râ   Úgateway.host_attachræ   rå   s    r!   Ú_host_gateway_ownerrè     s/   € ä	×	Ò	œY×	'Õ	'Ý4Ù‹~÷ 
(×	'×	'Ñ	'ñ ús	   £;»A	c               ó    € V ^8„  d   QhRRRR/# rÜ   r   )r    s   "r!   r"   r"     s   € ÷ ñ ˜4ð ¨=ñ r$   c                ó8  € \        V 4      pVe   Ve   W!P                  8w  d   V# \        P                  ! \        4      ;_uu_ 4        ^ RIHp V! \        VP                  4      \        V 4      4      '       d   TMRuuRRR4       #   + '       g   i     R# ; i)a  PID of a gateway ``home`` OWNS, else None.

Ownership is what this command may stop, and the host multiplexer is not owned by the profiles
it serves: it is one process, launched from one home. Deriving ownership from
:func:`_live_gateway_pid` made every secondary on a CONVERGED host report the host gateway's
PID, so the plan called the host half-migrated and each `hermes update` SIGTERMed the only
gateway it had. The host process counts only for the home it was actually launched from; every
other profile it serves owns nothing.
N)Ú_same_hermes_home)	rã   r   rà   rá   râ   rß   rë   r   r   )r   Úownerr   rë   s   &&  r!   Ú_own_gateway_pidrí     sk   € ô ˜DÓ
!€CØ
‚{�e’m s¯i©iÔ'7Øˆ
Ü	×	Ò	œY×	'Õ	'Ý4Ù'¬¨U¯Z©ZÓ(8¼$¸t»*×EÒE‰sÈ4÷ 
(×	'×	'Ñ	'ñ ús   Á5BÂB	c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   r   r   r   r   r   r   r   ztuple[Optional[int], Path]r   )r    s   "r!   r"   r"   #  s*   € ÷ 1ñ 1˜Dð 1 }ð 1Ð@Vð 1Ð[uñ 1r$   c                ó    € ^ RI Hp V! WV4      # )rš   )Úgateway_identity)Ú!hermes_cli.gateway_migrate_guardsrð   )r   r   r   rð   s   &&& r!   Ú_gateway_identityrò   #  s   € ÝBÙ˜D xÓ0Ð0r$   c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   r   r   )r    s   "r!   r"   r"   (  s   € ÷ ñ ˜dð Ð'=ñ r$   c                ó  a€ ^ RI Ho . pSP                  4       '       dƒ   ^ RIHpHp ^ RIHp ^ RIH	p V! V 4      ;'       g    RpV! 4       P                  V! V4      ,          pVP                  4       '       d'   VR8X  g   V! V4      '       d   VP                  R4       V# \        V 4      ;_uu_ 4        SP                  4       '       d   VP                  V3R lR	 4       4       SP!                  4       '       d6   SP#                  4       P%                  4       '       d   VP                  R
4       SP'                  4       '       d"   \)        4       '       d   VP                  R4       RRR4       V#   + '       g   i     T# ; i)u`  Every installed service for ``home``'s gateway (units / plist / scheduled task), user scope first.

Under s6 the footprint is the SLOT: the root slot always (it is what a restart goes through),
a named profile's slot only while it is UP â€” a registered-down slot is what the container's
boot leaves behind for every named profile and is not a gateway.©Úgateway)Únamed_slot_nameÚ
slot_is_up©ÚS6ServiceManager©Úprofile_name_for_homer&   c              3  ó|   <"  € T F1  pSP                  VR 7      P                  4       '       g   K+  RV3x € K3  	  R# 5i)©ra   r9   N)Úget_systemd_unit_pathÚexists)rC   ra   Úgws   & €r!   rE   Ú&_installed_services.<locals>.<genexpr>;  s3   øé € Ð}¹=°ÈB×LdÑLdÐlrÐLdÓLs×LzÑLz×L|Ô,˜) VÕ,»=ùs   ƒ(<°<N)rd   F)FT)ÚlaunchdF)re   F)Ú
hermes_clirö   Ú_running_under_s6Úhermes_cli.gateway_multiplex_s6r÷   rø   Úhermes_cli.service_managerrú   rÅ   rü   ÚscandirÚis_dirÚappendrÎ   Úsupports_systemd_servicesÚextendÚis_macosÚget_launchd_plist_pathr   Ú
is_windowsÚ_windows_task_installed)	r   Úfoundr÷   rø   rú   rü   r   Úslot_dirr  s	   &       @r!   Ú_installed_servicesr  (  s  ø€ õ )Ø$&€EØ	×Ñ×ÒßOÝ?Ý:Ù$ TÓ*×7Ð7¨iˆÙ#Ó%×-Ñ-±ÀÓ0EÕEˆØ�?‰?×Ò $¨)Ô"3±zÀ$×7GÒ7GØ�L‰L˜Ô'ØˆÜ	�4��Ø×'Ñ'×)Ò)Ø�L‰LÔ}¹=Ó}Ô}Ø�;‰;�=Š=˜R×6Ñ6Ó8×?Ñ?×AÒAØ�L‰LÐ+Ô,Ø�=‰=�?Š?Ô6×8Ò8Ø�L‰LÐ+Ô,÷ 
ð €L÷ 
Žð €Lús   Â5AE3Ã=#E3Ä!&E3Å!E3Å3F	c               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   r"   C  s   € ÷ ñ  ñ r$   c                 óò   € ^ RI Hp  \        P                  ! \        4      ;_uu_ 4        \        V P                  4       ;'       g    V P                  4       4      uuRRR4       #   + '       g   i     R# ; i)aª  Is a per-profile Windows gateway installed for the ACTIVE ``HERMES_HOME``?

``get_task_name()`` is home-suffixed, so this answers per profile exactly the way the systemd
unit path does. Either half counts: ``hermes gateway install`` falls back to a Startup-folder
entry when it cannot register a scheduled task, and a migration that removed only the task
would leave the fallback launching a second gateway at the next logon.
©Úgateway_windowsNF)r  r  rà   rá   râ   r   Úis_task_registeredÚis_startup_entry_installed)Úgwws    r!   r  r  C  sL   € õ 2Ü	×	Ò	œY×	'Õ	'Ü�C×*Ñ*Ó,×PÐP°×0NÑ0NÓ0PÓQ÷ 
(×	'×	'Ñ	'áús   ©1A%Á%A6	c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   r   r   r   r   r   )r    s   "r!   r"   r"   Q  s'   € ÷ Vñ V ð VÐ0Fð VÈ=ñ Vr$   c                óÆ   € RV9  d   R# ^ RI Hp \        V 4      ;_uu_ 4        VP                  VP	                  RR7      4      uuRRR4       #   + '       g   i     R# ; i)z<Read ``User=`` before migration removes a system-scope unit.TNrõ   rþ   r:   )r  rö   rÎ   Ú_read_systemd_user_from_unitrÿ   )r   r   r  s   && r!   Ú_systemd_service_userr  Q  sA   € à Ô(ÙÝ(Ü	�4��Ø×.Ñ.¨r×/GÑ/GÈtÐ/GÓ/TÓU÷ 
��‹ús   £!AÁA 	r   c               ó0   € V ^8„  d   QhRRRRRRRRRR	R
R/# )r   rf   r   ra   r   Úverbr   r   r   r   r   ÚNoner   )r    s   "r!   r"   r"   Z  s8   € ÷ -ñ -�cð - 4ð -¨sð -¸$ð -ÐP]ð -Ðimñ -r$   c          	     ó€  € V R8X  d   \        W#4      # ^ RIHp \        V4      ;_uu_ 4        VR8X  dX   V R8X  d   VP	                  4        M4V R8X  d   ^ RIHp VP                  RRR7       MVP                  WRR	7        R
R
R
4       R
# VR8X  d   V R8w  d    R
R
R
4       R
# VP                  RVP                  4       .VR^R7      p\        VR^ 4      ^ 8w  d+   \        RVP                  4        RVP                   R24      h R
R
R
4       R
# VP                  WV4       R
R
R
4       R
#   + '       g   i     R
# ; i)zd``stop`` / ``uninstall`` / ``start`` / ``restart`` / ``install`` / ``enable`` on ``home``'s service.rd   rõ   Úinstallr  re   r  T)Ú	start_nowÚstart_on_login)ra   r   Únon_interactiveNÚenabler9   F)ra   ÚcheckÚtimeoutÚ
returncodezcould not enable z" at boot (systemctl enable exited z)); a reboot would come up with no gateway)Ú_s6_slot_opr  rö   rÎ   Úlaunchd_installr  r#  Úsystemd_installÚ_run_systemctlÚget_service_nameÚgetattrÚRuntimeErrorr*  Ú_service_call)rf   ra   r   r   r   r  r  Úenableds   &&&&$   r!   Ú_service_opr4  Z  s5  € àˆt„|Ü˜4Ó&Ð&Ý(Ü	�4��Ø�9ÔØ�yÔ Ø×"Ñ"Õ$Ø˜Ô"õ >Ø—‘ d¸4�Õ@à×"Ñ"¨&Ð[_Ð"Ô`Ø÷ 
‰ð �8Ôð �yÔ Ø÷) 
‰ð* ×'Ñ'¨°2×3FÑ3FÓ3HÐ(IÐRXÐ`eÐoqÐ'ÓrˆGÜ�w ¨aÓ0°AÔ5Ü"Ð%6°r×7JÑ7JÓ7LÐ6Mð N-Ø-4×-?Ñ-?Ð,@Ð@ið$kó lð là÷3 
‰ð4 	×Ñ˜ VÔ,÷5 
��Šús   ¬AD,ÂD,Â$A"D,ÄD,Ä,D=	c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   r!  r   )r    s   "r!   r"   r"   |  s   € ÷  ñ   ð  ¨ñ  r$   c                ó"   € ^ RI Hp V! V 4       R# )rš   )Ú_stop_gateway_processN)r�   r7  )r   r7  s   & r!   r7  r7  |  s   € Ý9Ù˜$Ör$   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   r   r   r   r!  r   )r    s   "r!   r"   r"   �  s!   € ÷ ñ �cð  ð ¨$ñ r$   c                ó–   € ^ RI HpHp ^ RIHp V! V4      ;'       g    RpVR8X  d   V R9   d   V! 4        R# V R8X  d   V! V4       R# R# )a  The s6 leg of :func:`_service_op`. A named profile's slot is never uninstalled: it stays
registered DOWN (``down`` file) as the target of a later ``hermes -p X gateway start``, exactly
the shape the container's boot produces. The root slot is (re)started so it re-reads its config.)Úbring_root_slot_upÚpark_named_slotrû   r&   NÚstop)ÚstartÚrestart)r  r:  r;  rÅ   rü   )r   r   r:  r;  rü   r   s   &&    r!   r+  r+  �  sK   € ÷ TÝ6Ù  Ó&×3Ð3¨)€DØˆyÔØÐ'Ô'ÙÔ ÙØˆv„~Ù˜Öñ r$   c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   r   r   )r    s   "r!   r"   r"   �  s   € ÷ ,ñ , $ð ,¨4ñ ,r$   c                ó’   € ^ RI Hp \        V 4      ;_uu_ 4        VP                  4       uuRRR4       #   + '       g   i     R# ; i)rš   rõ   N)r  rö   rÎ   Ú_spawn_detached_gateway)r   r  s   & r!   rA  rA  �  s&   € Ý(Ü	�4��Ø×)Ñ)Ó+÷ 
��‹ús	   š5µA	c               ó    € V ^8„  d   QhRRRR/# )r   rq   r   r   r   r   )r    s   "r!   r"   r"   –  s   € ÷ 9ñ 9 tð 9°ñ 9r$   c                ó"   € ^ RI Hp V! V 4      RJ # )u  The operator's EXPLICIT opt-in only. The unset default (on) is settled by the default gateway at
boot and refused while a secondary runs its own gateway â€” exactly the fleet this command folds â€”
so the plan reads it as "not yet multiplexed" and the migration proceeds.)Úexplicit_multiplex_flagT)Ú!hermes_cli.gateway_multiplex_moderD  )rq   rD  s   & r!   Ú_read_multiplex_flagrF  –  s   € õ JÙ" <Ó0°DÐ8Ð8r$   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rq   r   Úvaluer   r   r!  r   )r    s   "r!   r"   r"   ž  s!   € ÷ 	.ñ 	.¨ð 	.°Tð 	.¸dñ 	.r$   c                ó„   € ^ RI HpHpHp V R,          pV! V4      pVP	                  RR4       V! VRV4       V! WV4       R# )z»Set ``gateway.multiplex_profiles`` in the DEFAULT profile's config.yaml through the config API
(same read-guard + nested-set + atomic write ``hermes config set`` uses; no raw YAML edits).)Ú_set_nestedÚ_write_user_configÚ$require_readable_config_before_writeúconfig.yamlÚmultiplex_profilesNzgateway.multiplex_profiles)Úhermes_cli.configrJ  rK  rL  rÊ   )rq   rH  rJ  rK  rL  Úcfg_pathÚuser_configs   &&     r!   Ú_write_multiplex_flagrR  ž  sC   € ÷ hÑgØ˜mÕ+€HÙ6°xÓ@€Kà‡O�OÐ(¨$Ô/Ù�Ð9¸5ÔAÙ�xÖ-r$   c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r    s   "r!   r"   r"   ­  s   € ÷ %ñ % $ñ %r$   c           	     óV  € ^ RI Hp ^ RIHp ^ RIHp ^ RIHp V! V 4      '       d   V! 4       M\        P                  ! 4       pV! V 4      ;_uu_ 4        V;_uu_ 4        V! 4       uuRRR4       uuRRR4       #   + '       g   i     M; iRRR4       R#   + '       g   i     R# ; i)uÍ  This profile's ``GatewayConfig`` read exactly the way the multiplexer reads it: under the
profile's own secret scope with multiplexing active, so a missing token stays missing instead of
borrowing the CLI process's ``os.environ`` (which holds the launch profile's ``.env``).

A PARKED profile stays in the inventory and the duplicate-credential guard (parking must not hide
a conflict), but it is inert for the host: its config is read without discovering its plugins,
whose ``register()`` would otherwise run in the live gateway on every boot (#123386). Only its
plugin-defined platforms fall out of the guard â€” their credentials cannot be evaluated without
importing the plugin; builtin bot tokens still collide.
)Úload_gateway_config)Ú_profile_runtime_scope)Úsuppress_plugin_discoveryr›   N)
Úgateway.configrU  Úgateway.runrV  Úhermes_cli.plugins_discoveryrW  r�   rœ   rà   Únullcontext)r   rU  rV  rW  rœ   Úscopes   &     r!   Ú_profile_gateway_configr]  ­  sb   € õ 3Ý2ÝFÝ5Ù+<¸T×+BÒ+BÑ%Ô'Ì
×H^ÒH^ÓH`€EÙ	 ×	%Õ	%§u¢uÙ"Ó$÷ (-¡u×	%Ò	%§u§uú×	%×	%×	%Ò	%ús$   ÁBÁA:	Á%
BÁ:B
ÂBÂB(	c               ó   € V ^8„  d   QhRR/# )r   r   r½   r   )r    s   "r!   r"   r"   Â  s   € ÷ 'ñ '˜nñ 'r$   c               #  óp   "  € ^ RI Hp Hp V ! 4       pV! R4        Rx € V! V4       R#   T! T4       i ; i5i)rš   )Úis_multiplex_activeÚset_multiplex_activeTN)Úagent.secret_scoper`  ra  )r`  ra  rË   s      r!   Ú_multiplex_read_moderc  Á  s1   é € çLÙ"Ó$€HÙ˜Ôð'Ûá˜XÖ&øÑ˜XÕ&üs   ‚6›) Ÿ
6©
3³6c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r    s   "r!   r"   r"   Ì  s   € ÷ ñ ¨/ñ r$   c           	     ó
  € \        V RR4      ;'       g    / p\        \        V RR4      ;'       g    \        V RR4      VP                  R4      VP                  R4      VP                  R4      VP                  R4      V R	7      # )
z»Config-shaped stand-in for ``GatewayRunner._adapter_credential_fingerprint`` (which probes
adapter attributes): token/api_key plus the id-style credentials adapters expose from ``extra``.ÚextraNrÍ   Úapi_keyÚapp_idÚ	client_idÚbot_idÚproject_secret)rÍ   Ú_app_idÚ
_client_idÚ_bot_idÚ_project_secretÚconfig)r0  r   rÈ   )Úplatform_configrf  s   & r!   Ú_credential_proberr  Ì  s{   € ô �O W¨dÓ3×9Ð9°r€EÜÜ�o w°Ó5×bÐb¼ÀÐR[Ð]aÓ9bØ—	‘	˜(Ó#°·	±	¸+Ó0FÐPU×PYÑPYÐZbÓPcØŸ	™	Ð"2Ó3¸Oôð r$   c               ó   € V ^8„  d   QhRR/# )r   r   zdict[tuple, str]r   )r    s   "r!   r"   r"   ×  s   € ÷ 
ñ 
Ð"2ñ 
r$   c                óø   € ^ RI Hp / pV P                  P                  4        FS  w  r4VP                  '       g   K  VP                  \        V4      4      pVf   K9  VP                  W#P                  V3&   KU  	  V# )zV``(platform, fingerprint)`` for every enabled platform with a discoverable credential.)ÚGatewayRunner)rY  ru  Ú	platformsÚitemsr3  Ú_adapter_credential_fingerprintrr  rH  )rp  ru  ÚclaimsÚplatformrq  Úfps   &     r!   Ú_credential_claimsr|  ×  sl   € å)Ø!€FØ%+×%5Ñ%5×%;Ñ%;Ö%=Ñ!ˆØ×&×&Ð&ÙØ×:Ñ:Ô;LÈ_Ó;]Ó^ˆØŒ>Ø+3¯>©>ˆF—N‘N BÐ'Ó(ñ &>ð €Mr$   c               ó    € V ^8„  d   QhRRRR/# )r   Úplatform_valuer   r   r   )r    s   "r!   r"   r"   ä  s   € ÷ <ñ <¨#ð <°#ñ <r$   c                óž   a € ^ RI Hp \        V 3R lV! 4       P                  4        4       4      pRP	                  V4      ;'       g    RS  R2# )z¬Env key NAMES (never values) that make ``platform_value`` connect as a bot, e.g.
``TELEGRAM_BOT_TOKEN``; the platform id when no key is registered (config.yaml-only token).)Úcredential_env_keysc              3  ó>   <"  € T F  w  rVS8X  g   K  Vx € K  	  R # 5ir4   r   )rC   Úkeyr   r~  s   &  €r!   rE   Ú(_credential_key_names.<locals>.<genexpr>è  s   øé € Ð^Ñ'D™8˜3ÈÈ~ÑH]—3’3Ó'Dùs   ƒ“
Ú/zthe z token)Úhermes_cli.profile_channelsr€  Úsortedrw  rI   )r~  r€  Únamess   f  r!   Ú_credential_key_namesrˆ  ä  sF   ø€ õ @ÜÔ^Ñ':Ó'<×'BÑ'BÔ'DÓ^Ó^€EØ�8‰8�E‹?×;Ð;  ^Ð$4°FÐ;Ð;r$   c               ó    € V ^8„  d   QhRRRR/# )r   Úconfigszlist[tuple[str, object]]r   rz   r   )r    s   "r!   r"   r"   ì  s   € ÷ ñ Ð(@ð ÀYñ r$   c                ó  € / p. pV  F  w  r4\        V4      P                  4        F]  w  rVVP                  WS4      pWs8X  d   K  \        V4      pVP	                  RV RV RV RV RV RV RV RV R	V R
\
         R24       K_  	  K�  	  V# )a  One finding per platform credential two profiles both hold, with the remedy. The SINGLE
source for the migrate preflight, ``hermes doctor`` and ``hermes gateway status``, so all three
name the same duplicates the same way: profile names + key names only, never a value or hash.z
Profiles 'z' and 'z' both hold the same z credential (z•): one platform token can serve only one gateway, so the bot answers from whichever profile claims it first and the other's adapter is parked. Give 'z' its own bot token, or remove z8 from the profile that should not own it (or keep it in z and route u;   's chats with profile_routes â€” gateway.profile_routes in z's config.yaml), then run Ú.)r|  rw  Ú
setdefaultrˆ  r
  r·   )	rŠ  ÚownersÚlinesr   ÚcfgÚclaimr~  rì   r‚  s	   &        r!   Úduplicate_credential_linesr’  ì  sÊ   € ð  "€FØ€EÛ‰	ˆÜ%7¸Ó%<×%BÑ%BÖ%DÑ!ˆEØ×%Ñ% eÓ2ˆEØŒ}ÙÜ'¨Ó7ˆCØ�L‰LØ˜U˜G 7¨4¨&Ð0EÀnÐEUÐUbÐcfÐbgð hLàLPÈ6ÐQpØ�%ÐOÐPUÈwÐVaÐbfÐagð hKØKPÈ'ÐQkÜ"Ð# 1ð&öó &Eñ ð €Lr$   c               ó   € V ^8„  d   QhRR/# )r   r   rz   r   )r    s   "r!   r"   r"     s   € ÷ 	/ñ 	/ yñ 	/r$   c                 óT  € . p \        4       ;_uu_ 4        \        4        FL  w  r\        P                  ! \        4      ;_uu_ 4        V P                  V\        V4      34       RRR4       KN  	  RRR4       \        V 4      #   + '       g   i     Kt  ; i  + '       g   i     L/; i)u  The preflight's duplicate-credential check read straight from the local profile homes, for
diagnostics that have no migration plan (doctor, gateway status). A profile whose gateway config
does not load is skipped here â€” ``build_migration_plan`` reports that one as its own blocker.N)rc  rÚ   rà   rá   râ   r
  r]  r’  )rŠ  r   r   s      r!   Úduplicate_credential_findingsr•    sx   € ð )+€GÜ	×	Õ	Ü(Ö*‰JˆDÜ×$Ò$¤Y×/Õ/Ø—‘ Ô&=¸dÓ&CÐDÔE÷ 0Ñ/ñ +÷ 
 ô & gÓ.Ð.÷ 0×/Ð/ú÷ 
 ×	ús#   •2BÁBÁ$BÂBÂ	BÂB'	c               ó$   € V ^8„  d   QhRRRRRR/# ©r   Úplanrp   rŠ  údict[str, object]r   r!  r   )r    s   "r!   r"   r"     s(   € ÷ Rñ R }ð RÐ?Pð RÐUYñ Rr$   c           
     óè   € V P                   P                  \        V P                   Uu. uF3  q"P                  V9   g   K  VP                  WP                  ,          3NK5  	  up4      4       R# u upi )u§   BLOCKER: the same bot credential configured on two profiles â€” the multiplexer would park
the duplicate adapter, so one profile's bot would go silent after migration.N)r{   r  r’  rs   r   )r˜  rŠ  rƒ   s   && r!   Ú_check_duplicate_credentialsr›    sT   € ð 	‡M�M×ÑÔ3Ø,0¯MªMÓO©M q¿V¹VÀwÑ=NÔ	"ˆ!�&‰&�'Ÿ&™&•/Ó	"©MÑOóQö RùÚOs   ©A/Á "A/c               ó    € V ^8„  d   QhRRRR/# )r   r~  r   r   r   r   )r    s   "r!   r"   r"     s   € ÷  ñ  °3ð  ¸4ñ  r$   c                ój  aaa€ ^ RI Ho R V3R llo\        P                  ! \        4      ;_uu_ 4        ^ RIHp ^ RIHpH	p VP                  V! V 4      4      pVe4   V! V^ ,          V^,          V^,          4      w  rVS! V4      uuRRR4       #  RRR4       \        P                  ! \        4      ;_uu_ 4        ^ RIHp V! 4        ^ RIHp VP                  V 4      p	V	e�   V	P                  p
S! V
4      '       d    RRR4       R	# ^ RIpVP#                  V
P$                  4      o\&        ;QJ d)    VV3R
 l\)        S4       4       F  '       g   K   R	M	  RM! VV3R
 l\)        S4       4       4      uuRRR4       #  RRR4       R#   + '       g   i     EL; i  + '       g   i     R# ; i)u#  True when the adapter for ``platform_value`` declares ``serves_profile_prefix`` (it answers
``/p/<profile>/...`` on the default listener). Read from the adapter CLASS â€” builtin table or the
plugin registry entry â€” never from a hand-kept list, so new ingress adapters count automatically.)ÚBasePlatformAdapterc               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   Ú4platform_serves_profile_prefix.<locals>.__annotate__  s   € ÷ :ñ :˜$ñ :r$   c                óˆ   <€ \        V \        4      ;'       d+    \        V S4      ;'       d    \        \	        V R R4      4      # )Úserves_profile_prefixF)Ú
isinstanceÚtypeÚ
issubclassr   r0  )Úclsrž  s   &€r!   Ú	_declaresÚ1platform_serves_profile_prefix.<locals>._declares  sF   ø€ Ü˜#œtÓ$÷ :ð :¬°CÐ9LÓ)M÷ :ð :ÔRVÜ�CÐ0°%Ó8óS:ð 	:r$   ©ÚPlatform)Ú_BUILTIN_ADAPTERSÚ_builtin_adapter_importN)Údiscover_plugins)Úplatform_registryTc              3  óH   <"  € T F  pS! \        SV4      4      x € K  	  R # 5ir4   )r0  )rC   r   r§  Úmodules   & €€r!   rE   Ú1platform_serves_profile_prefix.<locals>.<genexpr>5  s    øé € ÐPÁK¸D‘y¤¨°Ó!6×7Ð7ÃKùs   ƒ"F)Úgateway.platforms.baserž  rà   rá   râ   rX  rª  rY  r«  r¬  rÈ   Úhermes_cli.pluginsr­  Úgateway.platform_registryr®  Úadapter_factoryÚ	importlibÚimport_modulerU   ÚanyÚdir)r~  rª  r«  r¬  ÚspecÚadapter_clsÚ_okr­  r®  ÚentryÚfactoryr¶  rž  r§  r°  s   &           @@@r!   Úplatform_serves_profile_prefixr¿    sG  ú€ õ ;÷:ð :ô 
×	Ò	œY×	'Õ	'Ý+ßJØ ×$Ñ$¡X¨nÓ%=Ó>ˆØÒÙ6°t¸AµwÀÀQÅÈÈaÍÓQÑˆKÙ˜[Ó)÷ 
(Ò	'ð ÷	 
(ô 
×	Ò	œY×	'Õ	'õ 	8ÙÔÝ?Ø!×%Ñ% nÓ5ˆØÒØ×+Ñ+ˆGÙ˜×!Ò!Ø÷ 
(Ñ	'ó Ø×,Ñ,¨W×-?Ñ-?Ó@ˆFß“3ÕPÄCÈÄKÓP—3—3’3ÕPÄCÈÄKÓPÓP÷ 
(Ò	'ð ÷ 
(ñ ÷- 
(×	'Ð	'ú÷ 
(Ö	'ñ ús1   µAFÂ=AF!Ä F!Ä)F!Ä5F!Å!F!ÆF	Æ!F2	c               ó$   € V ^8„  d   QhRRRRRR/# )r   r~  r   rP   r   r   )r    s   "r!   r"   r"   9  s&   € ÷ Fñ F¨sð F¸Sð FÀSñ Fr$   c                ó  € ^ RI Hp / p\        P                  ! \        4      ;_uu_ 4        V P
                  P                  V! V4      4      ;'       g    \        / R7      P                  ;'       g    / pRRR4       RRRR/pVP                  VR4      w  rgVP                  R4      ;'       g    TpVP                  R4      ;'       g    TpRRRR	/P                  VR
4      p^ RI	H
p	 RV	! V4       RV RV V 2#   + '       g   i     L�; i)rš   r©  )rf  NÚ
api_serverÚwebhookÚhostÚportz/v1/...z/webhooks/<route>z/...)Úformat_url_hostzhttp://Ú:z/p/)z	127.0.0.1iÂ!  )z0.0.0.0iÄ!  )z<host>z<port>)rX  rª  rà   rá   râ   rv  rÈ   r   rf  Úhermes_cli.url_utilsrÆ  )
Údefault_cfgr~  rP   rª  rf  ÚdefaultsrÄ  rÅ  ÚtailrÆ  s
   &&&       r!   Ú_listener_urlrÌ  9  só   € Ý'Ø€EÜ	×	Ò	œY×	'Õ	'Ø×&Ñ&×*Ñ*©8°NÓ+CÓD×aÐaÌÐ^`ÔHa×hÑh×nÐnÐlnˆ÷ 
(àÐ1°9Ð>OÐP€HØ—‘˜nÐ.BÓC�J€DØ�9‰9�VÓ×$Ð$ €DØ�9‰9�VÓ×$Ð$ €DØ˜) YÐ0CÐD×HÑHÈÐY_Ó`€DÝ4à‘_ TÓ*Ð+¨1¨T¨F°#°g°Y¸t¸fÐEÐE÷ 
(×	'ús   «AC<Á2C<Ã<D	c               ó$   € V ^8„  d   QhRRRRRR/# r—  r   )r    s   "r!   r"   r"   H  s#   € ÷ ñ ¨ð Ð@Qð ÐVZñ r$   c                óà  € ^ RI Hp VP                  R4      pV P                   EFE  pVP                  VP                  4      pVf   K%  VP
                  P                  4        EF  w  rgVP                  '       d$   V! VP                  VP                  4      '       g   K=  \        VP                  4      '       dZ   V P                  P                  RVP                   RVP                   R\        W6P                  VP                  4       R24       K±  V P                  P                  RVP                   RVP                   R	VP                   R
VP                   R2	4       EK  	  EKH  	  R# )z³BLOCKER when a secondary enables a port-binding platform with no ``/p/<profile>/`` ingress
(the multiplexer skips the whole profile); NOTICE (URL changes) when the ingress exists.)Úplatform_binds_portr&   Nú	Profile 'z': z$ moves onto the default listener at zR (its key/secret is unchanged; update clients that call the old per-profile port).z
' enables z), which binds its own port and has no /p/zq/ ingress on the default listener yet; the multiplexer would skip the whole profile. Disable it there (platforms.z=.enabled: false) or add a /p/<profile>/ ingress for it first.)rX  rÏ  rÈ   r„   r   rv  rw  r3  rH  rf  r¿  r|   r
  rÌ  r{   )r˜  rŠ  rÏ  rÉ  rP   r�  rz  rq  s   &&      r!   Ú_check_secondary_port_bindersrÑ  H  s2  € õ 3Ø—+‘+˜iÓ(€KØ×#Õ#ˆØ�k‰k˜'Ÿ,™,Ó'ˆØŠ;ÙØ),¯©×)<Ñ)<×)>Ñ%ˆHØ"×*×*Ð*Ñ2EÀhÇnÁnÐVe×VkÑVk×2lÒ2lÙÜ-¨h¯n©n×=Ò=Ø—‘×#Ñ#Ø §¡˜~¨S°·±Ð0@Ð@dÜ$ [·.±.À'Ç,Á,ÓOÐPð QUðVöð —‘×$Ñ$Ø §¡˜~¨Z¸¿¹Ð7Gð HØ!Ÿ,™,˜ð (FØFNÇnÁnÐEUð V@ðA÷ô *?ó	 $r$   z>tuple[Callable[[MigrationPlan, dict[str, object]], None], ...]Ú_PREFLIGHT_CHECKSc               ó    € V ^8„  d   QhRRRR/# )r   r˜  rp   r   r™  r   )r    s   "r!   r"   r"   i  s   € ÷ ñ  ð Ð2Cñ r$   c                ó\  € / p\        4       ;_uu_ 4        V P                   F%  p \        VP                  4      WP                  &   K'  	  RRR4       V#   \
         d8   pT P                  P                  R TP                   RT R24        Rp?Kp  Rp?ii ; i  + '       g   i     T# ; i)rÐ  z&': could not load its gateway config (ú).N)rc  rs   r]  r   r   râ   r{   r
  )r˜  rŠ  rP   Úexcs   &   r!   Ú_load_profile_configsr×  i  s•   € Ø!#€GÜ	×	Õ	Ø—}”}ˆGðnÜ(?ÀÇÁÓ(M�Ÿ™Ó%ñ %÷ 
 ð €Nøô ô nØ—‘×$Ñ$ y°·±°Ð>dÐehÐdiÐikÐ%l×mÒmûðnú÷	 
 Ö	ð €Nús4   •B¦!AÁBÁB	Á ,B	ÂBÂB	ÂBÂB+	c               ó   € V ^8„  d   QhRR/# )r   r   rp   r   )r    s   "r!   r"   r"   t  s   € ÷ 'ñ '˜mñ 'r$   c                 óî  a€ ^ RI Hp  \        4       p\        4       p. p\	        4        FX  w  rE\        WR4      \        V4      rv\        WVV4      w  r‰TP                  \        YEYg\        WW4      TW•8X  d   RMT	R7      4       KZ  	  \        W\        V4      V ! V4      \        V4      R7      p
^ RIHp \	        4        UUu0 uF  w  rLVkK	  	  uppo\         ;QJ d    . V3R lV! RRR7       4       F  NK  	  5M! V3R lV! RRR7       4       4      V
n        V
P$                  ;'       d!    \'        V
P(                  V
P*                  4      V
n        \/        V
P0                  4      ^8  d   V
P2                  P                  R	4       V
# \5        V
4      p\6         F  pV! W­4       K  	  ^ R
IHp V
P2                  P=                  R V! V
4       4       4       V
P2                  P                  R4       V
# u uppi )zQEnumerate profiles + their OWN gateway footprint, then run every preflight check.©Úrecorded_served_profilesN)r   r   r   r   r   r   r   )rq   rs   rt   rv   rx   rÖ   c              3  óP   <"  € T F  w  rVR 8w  g   K  VS9  g   K  Vx € K  	  R# 5i)r&   Nr   )rC   r   Ú_homeÚfoldables   &  €r!   rE   Ú'build_migration_plan.<locals>.<genexpr>‰  s1   øé € ð &7Ù P‘�Ø�9Ñô 	Ø!%¨XÑ!5÷ 	ŠÓ Pùs   ƒ&“&œ
&T)Úinclude_standalonez.Only one profile exists: nothing to multiplex.)Úauto_migration_blockersc              3  ó,   "  € T F
  pR V 2x € K  	  R# 5i)z/Not migrated automatically by `hermes update`: Nr   )rC   Úbs   & r!   rE   rß  –  s   é € ÐuÑWtÐRSÐIÈ!ÈÕMÓWtùs   ‚z—Profiles created after the migration are served by the running multiplexer as soon as they exist (it rescans profiles/ on create/delete and every 30s).)Ú#hermes_cli.gateway_multiplex_servedrÛ  rÓ   rè   rÚ   rí   r  rò   r
  r   r  rp   rF  Ú_read_manifestr�   r×   Útupler}   rt   Ú_manifest_not_yet_servedrx   rv   ry   r‘   rs   r|   r×  rÒ  rñ   rá  r  )rÛ  rq   rì   rs   r   r   r   r   r   r   r˜  r×   rÝ  rŠ  r(  rá  rÞ  s                   @r!   Úbuild_migration_planrè  t  s³  ø€ åLÜ “?€LäÓ!€EØ€HÜ$Ö&‰
ˆÜ(¨Ó5Ô7JÈ4Ó7PˆXÜ-¨d¸ÓBÑˆØ�‰œ¨DÀÜ3HÈÓ3XÐ^aØ<HÔ<P±DÐVbôdö 	eñ 'ô Ø!Ü.¨|Ó<Ù,¨\Ó:Ü Ó-ô	€Dõ 6Ü(6Ô(8Ô9Ñ(8™˜“Ñ(8Ò9€Hß %¤ô &7Ù 1°$È4Õ Pó&7§¡ô &7Ù 1°$È4Õ Pó&7ó !7€DÔð ×-Ñ-×kÐkÔ2JÈ4Ï=É=ÐZ^×ZjÑZjÓ2k€DÔÜ
ˆ4�=‰=Ó˜AÔØ�‰×ÑÐLÔMØˆÜ# DÓ)€Gß"ˆÙˆdÖñ #åIð 	‡L�L×ÑÑuÑWnÐosÔWtÓuÔuØ‡L�L×Ñð	Lôð €Kùó' :s   Â>G1c               ó    € V ^8„  d   QhRRRR/# )r   r�  rz   r   r!  r   )r    s   "r!   r"   r"   ¡  s   € ÷ ñ �)ð  ñ r$   c                ó.   € V  F  p\        V4       K  	  R # r4   )Úprint)r�  Úlines   & r!   Ú_printrí  ¡  s   € ÛˆÜˆdŽó r$   c               ó    € V ^8„  d   QhRRRR/# ©r   r˜  rp   r   rz   r   )r    s   "r!   r"   r"   ¦  s   € ÷ ñ ˜mð °	ñ r$   c                ó<  € RR R llpV P                    Uu. uF0  q"P                  '       g   VP                  '       g   K(  V! V4      NK2  	  ppV P                  pVP                  '       g   VP                  '       d   VP	                  V! VR4      4       V# u upi )aX  Every RUNNING gateway process ``apply_migration`` will signal, in apply order.

Derived from the steps the apply actually takes, not from "secondaries that happen to expose a
PID". Two classes were silently missing and both killed live processes: a secondary with an
installed unit and no readable PID is stopped through ``_service_op`` (which SIGTERMs whatever
the supervisor is running), and the DEFAULT's own live gateway is replaced by
:func:`_restart_default`. A dry run that promises nothing will be stopped and then stops the
host's only gateway breaks the contract this block exists to keep.
c               ó$   € V ^8„  d   QhRRRRRR/# )r   rƒ   r   Úsuffixr   r   r   )r    s   "r!   r"   Ú)_signalled_gateways.<locals>.__annotate__°  s!   € ÷ -ñ -�ð -¨ð -°Sñ -r$   c                ó’   € V P                   '       d   R V P                    2MRV P                  4        2pV P                   RV V R2# )zpid zsupervised by z (rc   )r   rJ   r   )rƒ   rò  Úwheres   && r!   Ú_whoÚ!_signalled_gateways.<locals>._who°  sE   € Ø"#§%§% %�$�q—u‘u�g‘¨~¸a¿o¹oÓ>OÐ=PÐ-QˆØ—&‘&�˜˜E˜7 6 (¨!Ð,Ð,r$   z, restarted onto the new flag)Ú )r�   r   r   r&   r
  )r˜  rö  rƒ   Ú	signalledr&   s   &    r!   Ú_signalled_gatewaysrú  ¦  sv   € ÷-ð #'×"=Ò"=ÓUÑ"=˜QÇÇÀÈ!Ï*Í*”‘�a–Ñ"=€IÐUØ�l‰l€GØ××Ð˜7Ÿ;Ÿ;˜;Ø×Ñ™˜gÐ'FÓGÔHØÐùò	 Vs   —%BÁBc               ó$   € V ^8„  d   QhRRRRRR/# )r   r˜  rp   Údry_runr   r   rz   r   )r    s   "r!   r"   r"   »  s!   € ÷ /$ñ /$�mð /$°ð /$¸)ñ /$r$   c          
     óž  € V'       d   R MRpVRV P                    2RR.pV P                   FL  pTP                  RVP                  R RVP                  ;'       g    R:R	 RVP                  4        24       KN  	  V P                  '       d?   VP                  R
RP                  V P                  4       24       VP                  R4       VP                  R4       V P                  '       dS   TP                  RV P                  '       d    RRP                  V P                  4       R2MR,           R,           4       V# V P                  '       d,   VP                  RV P                   \        ,           R24       M5V P                  '       d$   V P                  '       d   VP                  R4       . p\        V 4      pV P                   F  pRP                  R VP                  '       d   RVP                   2MRRP                  R VP                   4       4      3 4       4      pVP                  RVP                   RV 24       K�  	  \!        V P                  4      ^8  d   V\#        V 4      ,           # V'       g   VP                  R4       MVR.VO,          pVP                  RV P                   R,           24       V P$                  e   \'        V 4      ^ ,          MV P)                  4       pTP                  RV P*                  P,                  '       d   R MR! R"2V'       d   R#V^ ,           2MR$,           R%\!        V P.                  4       R&2,           4       VP                  R'V P                   \        ,           R(24       V'       d*   VRR)RP                  V4      ,           R,           R*.,          pV\#        V 4      ,           # )+u,   Migration plan (dry run â€” nothing changed)zMigration planz  default home: rø  z$  profile      gateway pid   servicez  z<12Ú Ú-z<13z?  Standalone by config (gateway.standalone: true), left alone: ú, zg    (temporary compatibility shim; remove the key and re-run once the gaps it covers for you are fixed)u1     âœ“ The default gateway is already multiplexingz
 (serving rc   z
 (flag on)rŒ  uv     â†» An earlier migration was interrupted before the default gateway came up (flag on, no live multiplexer; manifest z); this run resumes it.up     â†» Half-migrated host: the flag is on, but the profile(s) below still own a gateway. This run converges them.r?   c              3  ó8   "  € T F  q'       g   K  Vx € K  	  R # 5ir4   r   )rC   Úxs   & r!   rE   Úformat_plan.<locals>.<genexpr>Ó  s$   é € ð  \ñ  &V ó  Z[Ÿ!š!ó  &Vùs   ‚	�
z	stop pid c              3  óT   "  € T F  p\        V4       R \        V4       2x € K   	  R# 5i)rþ  N)rl   rA   rB   s   & r!   rE   r  Ó  s?   é € ð  WUñ  JTð  EFÔZfÐghÓZiÐYjÐjkÔlzÐ{|Ól}Ðk~ÕWó  JTùs   ‚&(z  - z: zR  No secondary profile runs its own gateway; the only step is turning the flag on:z  Steps:z5  - default: set gateway.multiplex_profiles: true in rM  z  - default: r>  r=  z the gatewayz via z (detached)z, verify it serves z	 profilesz!  - record the previous state in zH (used to undo a FAILED apply, and to resume this command after a crash)u1     âš  This SIGTERMs running gateway process(es): zb    They drain in-flight turns and exit; their profiles are served by the host gateway afterwards.)rq   rs   r
  r   r   rJ   r}   rI   r’   rv   ry   ÚMANIFEST_NAMErt   r�   rú  r   r‘   Ú
_plan_tailrx   Ú_resume_targetr¨   r&   r5   rž   )	r˜  rü  Úheadr�  rƒ   Ústepsrù  ÚwhatÚtargets	   &$       r!   Úformat_planr  »  s’  € ß=DÑ9ÐJZ€DØÐ% d×&7Ñ&7Ð%8Ð9¸2Ð?eÐf€EØ�]Œ]ˆØ�‰�r˜!Ÿ&™& ˜ Q q§u¡u§| |°Ð&:°cÐ&:¸!¸A¿O¹OÓ<MÐ;NÐOÖPñ à× × Ð Ø�‰ÐVØŸ	™	 $×";Ñ";Ó<Ð=ð?ô 	@à�‰ð 1ô 	2à	‡L�L�ÔØ××ÐØ�‰ÐHØGK×GW×GWÐGW˜* T§Y¡Y¨t×/?Ñ/?Ó%@Ð$AÀÑCÐ]iõkØmpõqô 	ràˆØ××ÐØ�‰ð @Ø@D×@QÑ@QÔTaÕ@aÐ?bÐbyð{õ 	|à	×	×	Ð	 D×$?×$?Ð$?Ø�‰ð 9ô 	:à€EÜ# DÓ)€IØ×(Ô(ˆØ�z‰zñ  \¸Q¿U¿U¸U¨	°!·%±%°Ñ&9ÈÈEÏJÉJñ  WUð  JK÷  JTò  JTó  WUó  MUñ  &Vó  \ó  \ˆØ�‰�t˜AŸF™F˜8 2 d VÐ,Ö-ñ )ô ˆ4�=‰=Ó˜AÔØ”z $Ó'Õ'Ð'ßØ�‰ÐiÕjà�*Ð%˜uÐ%Õ%ˆØ	‡L�LÐHÈ×IZÑIZÐ]jÕIjÐHkÐlÔmð )-¯©Ò(AŒ^˜DÓ! !Ö$Àt×G_ÑG_ÓGa€FØ	‡L�L�=¨d¯l©l×.F×.FÐ.F¡ÈGÐ TÐT`Ðaß+1�e˜F 1�I˜;Ñ'°}õFØJ]Ô^aÐbf×b|Ñb|Ó^}Ð]~ð  Hð  IIõIô Jà	‡L�LÐ4°T×5FÑ5FÌÕ5VÐ4Wð X[ð \ô ]çð 	�"ØEÈÏ	É	ÐR[ÓH\Õ\Ð_bÕbð(ð)õ 	)ˆð ”:˜dÓ#Õ#Ð#r$   c               ó    € V ^8„  d   QhRRRR/# rï  r   )r    s   "r!   r"   r"   í  s   € ÷ ñ �]ð  yñ r$   c                óŒ  € . pV P                   '       dd   V P                  f   VRR.,          pM$VRR\        V P                  4       R2RR.,          pYP                    Uu. uF  pRV 2NK
  	  up,          pV P                  '       d1   VRR.,          pYP                   Uu. uF  pRV 2NK
  	  up,          pV# u upi u upi )	Nrø  u:     âœ— Blockers (fix these first, nothing will be changed):uT     âš  Blockers found, but a migration is already in progress on this host (manifest rÕ  uY       This run RESUMES it and converges anyway â€” refusing would strand the profiles whosezF    gateways an earlier attempt already removed. Fix these afterwards:õ       â€¢ z
  Notices:)r{   rx   Ú_manifest_pathrq   r|   )r˜  r�  rã  Úns   &   r!   r  r  í  sÐ   € Ø€EØ‡}‡}€}Ø�=‰=Ò Ø�bÐVÐWÕW‰Eð �bð#Ü#1°$×2CÑ2CÓ#DÐ"EÀRðIð'à^ð`õ `ˆEð 	¯-ª-Ó8©- Q�H˜Q˜C“.©-Ñ8Õ8ˆØ‡|‡|€|Ø�"�lÐ#Õ#ˆØ¯,ª,Ó7©, Q�H˜Q˜C“.©,Ñ7Õ7ˆØ€Lùò	 9ùò 8s   Á!B<Â#Cc               ó    € V ^8„  d   QhRRRR/# )r   rx   rN   r   zOptional[list[dict]]r   )r    s   "r!   r"   r"     s   € ÷ ñ  Dð Ð-Añ r$   c                óÎ   € V P                  R. 4      p\        V\        4      '       d;   \        ;QJ d    R V 4       F  '       d   K   RM	  RM! R V 4       4      '       g   R# V# )zWThe manifest's secondary records, or None when the (hand-edited) manifest is malformed.r„   c              3  ó¦   "  € T FG  p\        V\        4      ;'       d+    VP                  R 4      ;'       d    VP                  R4      x € KI  	  R# 5i)rP   r   N)r£  rN   rÈ   ©rC   Úrs   & r!   rE   Ú(_manifest_secondaries.<locals>.<genexpr>  sC   é € Ð,vÑquÐlm¬Z¸¼4Ó-@×-gÐ-gÀQÇUÁUÈ9ÓEU×-gÐ-gÐZ[×Z_ÑZ_Ð`fÓZgÔ-gÓquùs   ‚AžA·AFTN)rÈ   r£  rZ   Úall)rx   Úrecss   & r!   Ú_manifest_secondariesr    sI   € à�<‰<˜ rÓ*€DÜ�dœD×!Ò!¯«Ñ,vÑquÓ,v¯¯ªÑ,vÑquÓ,v×)vÒ)vÙØ€Kr$   c               ó   € V ^8„  d   QhRR/# r,   r   )r    s   "r!   r"   r"   
  s   € ÷ ñ Ð#=ñ r$   c                ó¸   € \        V \        4      '       dD   V P                  R 4      '       d-   \        V R ,          4      \	        V P                  R4      4      3# R# )rf   ra   N)r£  rN   rÈ   r   r   rk   s   &r!   Ú_service_from_dictr  
  sB   € Ü�'œ4× Ò  W§[¡[°×%8Ò%8Ü�7˜6•?Ó#¤T¨'¯+©+°hÓ*?Ó%@Ð@Ð@Ùr$   c               ó    € V ^8„  d   QhRRRR/# )r   ÚrecrN   r   r   r   )r    s   "r!   r"   r"     s   € ÷ 4ñ 4˜Dð 4Ð%;ñ 4r$   c                óÜ   € V P                  R4      p\        V\        4      '       d   R V 4        Uu. uF
  q"f   K  VNK  	  up# \        V P                  R4      4      pVe   V.# . # u upi )z”Every service a manifest record names: ``services`` (all installed units) or, in a manifest
written before that key existed, the single ``service``.r   c              3  ó8   "  € T F  p\        V4      x € K  	  R # 5ir4   )r  r  s   & r!   rE   Ú%_recorded_services.<locals>.<genexpr>  s   é € ÐD¹8°aÔ.¨q×1Ð1»8ùrG   r0   )rÈ   r£  rZ   r  )r  ÚrecordedrD   r0   s   &   r!   Ú_recorded_servicesr$    sh   € ð �w‰w�zÓ"€HÜ�(œD×!Ò!ÙD¹8ÔDÓVÑD�a—�ÑDÑVÐVÜ  §¡¨Ó!3Ó4€GØÒ+ˆGˆ9Ð3°Ð3ùò Ws
   ³A)½A)c               ó    € V ^8„  d   QhRRRR/# )r   r  rN   r   r   r   )r    s   "r!   r"   r"     s   € ÷ <ñ <˜tð <¨ñ <r$   c                óh   € V P                  R 4      p\        V\        4      '       d   V'       d   V# R# )r   N)rÈ   r£  r   )r  rb   s   & r!   Ú_recorded_run_as_userr'    s)   € Ø�7‰7�=Ó!€DÜ˜d¤C×(Ò(¯Tˆ4Ð;°tÐ;r$   c               ó    € V ^8„  d   QhRRRR/# )r   rx   rN   r   ú0tuple[Optional[tuple[str, bool]], Optional[str]]r   )r    s   "r!   r"   r"     s   € ÷ ñ  Dð Ð-]ñ r$   c                ó  a€ V P                  R4      .\        V 4      ;'       g    . O Uu. uF  p\        V\        4      '       g   K  VNK  	  pp\	        R V 4       R4      p\	        V3R lV 4       R4      pW43# u upi )z°Service manager + ``User=`` the resumed default install should use, read from the recorded
footprint (the units themselves are gone by the time an interrupted apply is re-run).r&   c              3  óH   "  € T F  p\        V4       F  q"x € K  	  K  	  R # 5ir4   )r$  )rC   r  rD   s   &  r!   rE   Ú(_target_from_manifest.<locals>.<genexpr>#  s   é € ÐB™d˜Ô,>¸qÖ,A q”1Ñ,A‘1›dùs   ‚ "Nc              3  óJ   <"  € T F  p\        V4      ;of   K  Sx € K  	  R # 5ir4   )r'  )rC   r  Úus   & €r!   rE   r,  $  s    øé € ÐP™D�qÔ*?ÀÓ*BÐ%B Q—’›Dùs   ƒ#™
#)rÈ   r  r£  rN   rŒ   )rx   r  r  r  rb   r.  s   &    @r!   Ú_target_from_manifestr/    s|   ø€ ð !Ÿ™ YÓ/ÐZÔ3HÈÓ3R×3XÐ3XÐVXÑZÓrÑZ�!Ô^hÐijÔlp×^q�AˆAÑZ€DÐrÜÑB™dÓBÀDÓI€FÜÔP™DÓPÐRVÓW€DØˆ<Ðùò ss   «A<ÁA<z+no gateway was recorded; nothing to restorec               ó$   € V ^8„  d   QhRRRRRR/# )r   r˜  rp   Úauto_blockersrz   r   r   )r    s   "r!   r"   r"   +  s!   € ÷ ñ  ð ¸ið ÈIñ r$   c                óp   € R R.. V P                   OVO Uu. uF  pRV 2NK
  	  upOR\         2NRN# u upi )u]   âš  Your profiles each run their own gateway. A single multiplexed gateway is the recommendedz?  setup, but this install cannot be migrated automatically yet:r  z   After fixing the above, run:  zF  (`hermes update` will migrate automatically once nothing blocks it.))r{   r·   )r˜  r1  rã  s   && r!   Úformat_update_warningr3  +  se   € àgØIðð #C D§M¡MÐ"B°MÑ"BÓ	CÑ"B˜QˆH�Q�C‹.Ñ"BÑ	Cðð +¬?Ð*;Ð<ð	ð
 	Qðð ùò 
Ds   –3c               ó    € V ^8„  d   QhRRRR/# )r   rq   r   r   r   )r    s   "r!   r"   r"   8  s   € ÷ (ñ ( ð (¨$ñ (r$   c                ó   € V \         ,          # r4   )r  )rq   s   &r!   r  r  8  s   € Øœ-Õ'Ð'r$   c               ó    € V ^8„  d   QhRRRR/# )r   rq   r   r   rw   r   )r    s   "r!   r"   r"   <  s   € ÷ 4ñ 4 ð 4¨.ñ 4r$   c                óú   € \        V 4      pVP                  4       '       g   R #  \        P                  ! VP	                  RR7      4      p\        T\        4      '       d   T# R #   \
        \        3 d     R # i ; i)Nz	utf-8-sig)Úencoding)	r  r   ÚjsonÚloadsÚ	read_textÚOSErrorÚ
ValueErrorr£  rN   )rq   ÚpathÚdatas   &  r!   rå  rå  <  sj   € Ü˜,Ó'€DØ�;‰;�=Š=ÙðÜ�zŠz˜$Ÿ.™.°+˜.Ó>Ó?ˆô ˜d¤D×)Ò)ˆ4Ð3¨tÐ3øô ”ZÐ ô Úðús   ¥&A% Á%A:Á9A:c               ó$   € V ^8„  d   QhRRRRRR/# )r   rx   rw   rv   ru   r   r   r   )r    s   "r!   r"   r"   G  s#   € ÷ 2ñ 2 ~ð 2ÐDWð 2Ð\`ñ 2r$   c           	     óâ  € V f   R# ^ RI Hp V P                  R4      .\        V 4      ;'       g    . O Uu. uF  p\	        V\
        4      '       g   K  VNK  	  ppV Uu0 uF^  pVP                  R4      '       d!   V! \        VR,          4      4      '       d   K:  \        VP                  R4      ;'       g    R4      kK`  	  upR0,          pT\        T;'       g    . 4      8:  * # u upi u upi )uï  The postcondition ``apply_migration`` waits for, re-derived from live state: a LIVE default that
recorded serving every unparked profile the manifest migrated. Anything less â€” no live gateway, an
installed-but-dead unit (``systemd_install`` writes the unit before the start that can still fail),
a standalone default never restarted â€” is a half-applied migration, not "already multiplexed".
Profiles created after the migration are not in the manifest, so they cannot flag it as interrupted.Fr›   r&   r   rP   )	r�   rœ   rÈ   r  r£  rN   r   r   Úset)rx   rv   rœ   r  r  Úmigrateds   &&    r!   rç  rç  G  sÖ   € ð ÒÙÝ5Ø Ÿ™ YÓ/ÐZÔ3HÈÓ3R×3XÐ3XÐVXÑZÓrÑZ�!Ô^hÐijÔlp×^q�AˆAÑZ€DÐrÙ<@ó P¹D°qØ—u‘u˜V—}’}Ñ,=¼dÀ1ÀVÅ9»o×,Nô 3”�A—E‘E˜)Ó$×1Ð1¨	Ö2¹Dñ PØS\ÐR]õ^€Hàœ3˜{×0Ð0¨bÓ1Ò1Ð1ùò sùòPs#   ¶C'ÁC'ÁC,Á;C,ÂC,Â8C,c               ó$   € V ^8„  d   QhRRRRRR/# )r   rq   r   r?  rN   r   r!  r   )r    s   "r!   r"   r"   V  s!   € ÷ :ñ : $ð :¨dð :°tñ :r$   c                ó6   € ^ RI Hp V! \        V 4      V4       R# )rš   ©Úatomic_json_writeN)ÚutilsrG  r  )rq   r?  rG  s   && r!   Ú_write_manifestrI  V  s   € Ý'á”n \Ó2°DÖ9r$   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rq   r   Úsecondary_namesr˜   r   r!  r   )r    s   "r!   r"   r"   \  s'   € ÷ Iñ I°ð IÀxð IÐTXñ Ir$   c                óÊ  € ^ RI Hp ^ RIHp V R,          pV! V4      pVf   R# . VR&   VP	                  R4      p\        V\        4      '       dƒ   \        ;QJ d    . R V 4       F  NK  	  5M! R V 4       4      pVP                  4        UU	u/ uF6  w  r‰\        V\        4      '       d   VP                  V4      '       d   K4  W‰bK8  	  up	pVR&   V! WERR	R7       R# u up	pi )
zORemove only status owned by multiplexing, without re-stamping gateway identity.)Úread_runtime_statusrF  zgateway_state.jsonNÚserved_profilesrv  c              3  ó>   "  € T F  q'       g   K  V R 2x € K  	  R# 5i)rÇ  Nr   )rC   r   s   & r!   rE   Ú0_reconcile_standalone_runtime.<locals>.<genexpr>h  s   é € ÐH±¨Ã4œ˜D˜6 �³ùs   ‚	�)ÚindentÚ
separators)Ú,rÇ  )rß   rM  rH  rG  rÈ   r£  rN   ræ  rw  r   Ú
startswith)
rq   rK  rM  rG  r>  Úruntimerv  Úprefixesr‚  rH  s
   &&        r!   Ú_reconcile_standalone_runtimerW  \  sÃ   € å2Ý'àÐ.Õ.€DÙ! $Ó'€GØ‚ÙØ!#€GÐÑØ—‘˜KÓ(€IÜ�)œT×"Ò"ß”5ÑH±ÓH—5‘5ÑH±ÓHÓHˆà)2¯©Ô):ô 
Ù):™:˜3Ü˜s¤C×(Ò(¨S¯^©^¸H×-Eô ˆCŠJÑ):ò 
ˆ�Ññ �d¨D¸Z×Hùó	 
s   Â1CÃCc               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   rq   r   Úexpectedr˜   r)  Úfloatr   ru   r   )r    s   "r!   r"   r"   p  s)   € ÷ ñ  4ð °8ð Àeð ÐPcñ r$   c                óL  € ^ RI Hp \        P                  ! 4       V,           pRp\        P                  ! 4       V8  dS   \	        V 4      ;_uu_ 4        V! V 4      pRRR4       Ve   V\        V4      8:  d   V# \        P                  ! R4       Kl  V#   + '       g   i     L@; i)zXPoll the default's ``gateway_state.json`` until ``served_profiles`` covers ``expected``.rÚ  Nç      à?)rä  rÛ  ÚtimeÚ	monotonicrÎ   rB  Úsleep)rq   rY  r)  rÛ  ÚdeadlineÚserveds   &&&   r!   Ú_wait_for_servedrb  p  ss   € åLÜ�~Š~Ó 'Õ)€HØ"&€FÜ
�.Š.Ó
˜XÔ
%Ü�|×$Õ$Ù-¨lÓ;ˆF÷ %àÒ (¬c°&«kÔ"9ØˆMÜ�
Š
�3ŽØ€M÷ %×$ús   Á	BÂB#	c          
     ó,   € V ^8„  d   QhRRRRRRRRR	R
/# )r   Úplan_defaultr   r  r-   rq   r   r   r   r   r   r   )r    s   "r!   r"   r"   ~  sA   € ÷ Sñ SØ ðSà&ðSð ðSð
 ðSð 	ñSr$   c               óF  € V P                   e!   V P                   w  rE\        WERV4       RV 2# Ve%   Vw  rE\        WERW#R7       \        WERV4       RV 2# V P                  e   RMRpV P                  e   \        V4       \	        V4      '       g   \        R	4      hV R
2# )a7  Bring the default gateway up on the new flag value; returns a one-line description.

Boot enablement is NOT this function's job: an existing survivor unit is enabled by
:func:`_enable_default_at_boot` before the first removal, and the install branch enables
through ``systemd_install``'s own enable-on-startup.
r>  z"restarted the default gateway via r#  r®   r=  z.installed and started the default gateway via Ú	restartedÚstartedz.could not spawn the default gateway (detached)z> the default gateway (detached; no service manager was in use))r0   r4  r   r7  rA  r1  )rd  r  rq   r   rf   ra   r   s   &&&$   r!   Ú_restart_defaultrh  ~  s³   € ð ×ÑÒ'Ø#×+Ñ+‰ˆÜ�D )¨\Ô:Ø3°D°6Ð:Ð:ØÒØ‰ˆÜ�D )¨\ÕSÜ�D '¨<Ô8Ø?À¸vÐFÐFØ&×*Ñ*Ò6‰;¸I€DØ×ÑÒ#Ü˜lÔ+Ü" <×0Ò0ÜÐKÓLÐLØˆVÐQÐRÐRr$   c               ó    € V ^8„  d   QhRRRR/# )r   r˜  rp   r   r   r   )r    s   "r!   r"   r"   œ  s   € ÷ ñ  -ð °Mñ r$   c                óÔ   € V P                   P                  f   R# V P                   P                  w  r \        WRV P                  4       R#   \         d   pRT 2u Rp?# Rp?ii ; i)aÕ  Make the survivor boot-startable BEFORE anything is removed. Every secondary unit the fold
uninstalls takes its boot enablement with it, and the pre-existing default unit may have been
disabled all along; ``restart`` on a disabled unit succeeds, so the old order (remove, then
restart) turned N boot-startable gateways into 0 and reported success (#124922). ``enable``
never starts the unit, so doing it first cannot double-bind a platform while secondaries run,
and an apply interrupted anywhere after it still leaves a host that comes back after a reboot.
Returns the failure line, or None: a fresh apply refuses on it (nothing has been removed yet),
a resume reports it and converges anyway (rollback could do no better).
Nr'  ú	default: )r&   r0   r4  rq   r1  )r˜  rf   ra   rÖ  s   &   r!   Ú_enable_default_at_bootrl  œ  sc   € ð ‡|�|×ÑÒ#ÙØ—<‘<×'Ñ'�L€Dð!Ü�D (¨D×,=Ñ,=Ô>ñ øô ô !Ø˜3˜%Ð Õ ûð!ús   ´A ÁA'ÁA"ÁA'Á"A'c               ó    € V ^8„  d   QhRRRR/# )r   r˜  rp   r   r!  r   )r    s   "r!   r"   r"   °  s   € ÷ 	Oñ 	O ]ð 	O°tñ 	Or$   c                ó®  € V P                    FÄ  pVP                   Ff  w  r#\        W#R VP                  4       \        W#RVP                  4       VR8X  d   RMRp\	        RVP
                   RV R\        W#34       24       Kh  	  VP                  f   K‰  \        VP                  4       \	        RVP
                   R	VP                   R
24       KÆ  	  R# )r<  rj   rd   zparked (down file)Úremovedõ     âœ“ z: stopped and z its Nz": stopped standalone gateway (pid rc   )	r�   r   r4  r   rë  r   rA   r   r7  )r˜  rƒ   rf   ra   Údones   &    r!   Ú_remove_secondary_gatewaysrr  °  s¯   € Ø×(Ô(ˆØŸJœJ‰LˆDÜ˜ f¨a¯f©fÔ5Ü˜ k°1·6±6Ô:Ø+/°4¬<Ñ'¸YˆDÜ�F˜1Ÿ6™6˜( .°°°e¼NÈDÈ>Ó<ZÐ;[Ð\Ö]ñ	 'ð
 �5‰5ÔÜ! !§&¡&Ô)Ü�F˜1Ÿ6™6˜(Ð"DÀQÇUÁUÀGÈ1ÐMÖNó )r$   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r˜  rp   r  r-   r   r   r   r   )r    s   "r!   r"   r"   ¼  s+   € ÷ ñ ˜=ð Ð2Lð Ð[hð Ðmzñ r$   c                ó¶  € ^ RI Hp ^ RIHp  V! V P                  R,          4       TR8H  ;'       gI    \        ;QJ d&    R T P                   4       F  '       g   K   RM	  R	M! R T P                   4       4      pT'       d    TP                  R
4       T P                  P                  f7   TR8X  d0   Tf    TP                  4        R# ^ RIp TP                  T4       R# R#   \
         d   pRT R2u Rp?# Rp?ii ; i  \
         d   p\        T4      u Rp?# Rp?ii ; i  \         d   pRT 2u Rp?# Rp?ii ; i  \          d
    RT R2u # i ; i)aH  A failure of the destructive phase that is knowable from the plan alone, refused BEFORE any
working per-profile gateway is stopped: rollback is the fallback for surprises, not the plan.
Mirrors the checks ``systemd_install``/``_service_call`` make on a system unit (root, resolvable
``User=``) and the config write's read-guard.rõ   )rL  rM  z(default: config.yaml cannot be updated (rc   NTc              3  ó8   "  € T F  qP                   x € K  	  R # 5ir4   )r;   rŠ   s   & r!   rE   Ú#_preflight_apply.<locals>.<genexpr>Ç  s   é € Ð<tÑXsÐST×=NÖ=NÓXsùrG   FÚ	migrationrk  z$default: the recorded service user 'z' does not exist on this hostr:   )r  rö   rO  rL  rq   râ   r¸  r�   Ú _require_root_for_system_servicer   r&   r0   Ú_system_service_identityr=  ÚpwdÚgetpwnamÚKeyError)r˜  r  r   r  rL  rÖ  Útouches_system_unitrz  s   &&&     r!   Ú_preflight_applyr~  ¼  sG  € õ
 )ÝFðAÙ,¨T×->Ñ->ÀÕ-NÔOð !Ð$5Ñ5×tÐt¿»Ñ<tÐX\×XsÒXsÓ<t¿¿ºÑ<tÐX\×XsÒXsÓ<tÓ9tÐßð	Ø×/Ñ/°Ô<ð ‡|�|×ÑÒ#¨Ð2CÔ(CØÒð)Ø×+Ñ+Ô-ñ ó ðiØ—‘˜[Ô)ó øô) ô AØ9¸#¸¸aÐ@Õ@ûðAûô ô 	Ü�s“8�Oûð	ûô ô )Ø" 3 %Ð(Õ(ûð)ûô ô iØ=¸k¸]ÐJgÐhÒhðiúsk   ŽC& ÂD Â:D' ÃE Ã&DÃ1C<Ã6DÃ<DÄD$Ä
DÄD$ÄD$Ä'EÄ2D<Ä6EÄ<EÅEÅEc               ó    € V ^8„  d   QhRRRR/# )r   r˜  rp   r   r)  r   )r    s   "r!   r"   r"   Ü  s   € ÷ 5ñ 5˜ð 5Ð+[ñ 5r$   c                óÀ   € V P                  4       V P                  4       r!V P                  f   W3# \        V P                  4      w  r4T;'       g    TT;'       g    T3# )z§Service manager + ``User=`` an apply will use: the manifest's record wins on a resume (the
live units it describes are already gone), else what the plan can still see.)r¨   r°   rx   r/  )r˜  r  r   Úm_targetÚm_users   &    r!   r  r  Ü  s\   € ð ×2Ñ2Ó4°d×6MÑ6MÓ6OˆKØ‡}�}ÒØÐ"Ð"Ü,¨T¯]©]Ó;Ñ€HØ×Ð�v˜v×4Ð4¨Ð4Ð4r$   c               ó    € V ^8„  d   QhRRRR/# rï  r   )r    s   "r!   r"   r"   æ  s   € ÷ 1ñ 1˜=ð 1À)ñ 1r$   c                ó°   € . V P                   Op\        WV4      pVe   VP                  V4       V'       g   . # RR.V Uu. uF  pRV 2NK
  	  upO# u upi )uf  Safety checks re-run on a RESUME and reported as findings instead of refusals.

Both halves matter. Skipping them (the resume branch used to return before ``_preflight_apply``
ran at all) let a duplicate bot credential, a ``/p/`` ingress gap or an unresolvable service
user reach the multiplexer unannounced. Enforcing them as blockers is worse: a resume runs on
a host whose secondary units are ALREADY removed, so refusing does not prevent damage â€” it
makes the damage permanent, which is exactly how a stranded host lost its only documented
recovery. So: run every check, name every finding, converge anyway.
uX     âš  Preflight findings â€” resuming anyway (this host is mid-migration; refusing wouldzO    leave its profiles with no gateway at all). Fix these once it is converged:r  )r{   r~  r
  )r˜  r  r   ÚfindingsÚapply_blockerÚfs   &&&   r!   Ú_resume_findingsrˆ  æ  sh   € ð  �—‘Ð€HÜ$ T°;Ó?€MØÒ Ø�‰˜Ô&ßØˆ	ØfØ]ð1á&.Ó/¡h �˜˜‹n¡hÑ/ð1ð 1ùâ/s   ÁAÚserved_waitc               ó$   € V ^8„  d   QhRRRRRR/# )r   r˜  rp   r‰  rZ  r   r   r   )r    s   "r!   r"   r"   û  s'   € ÷ Lñ L˜-ð L¸ð LÐZ^ñ Lr$   c               óª  € V P                   '       d   \        R4       R# \        V 4      w  r#V P                  pVeV   \        R\	        V P
                  4       24       \        \        WV4      4       \        V 4      pVe   \        RV R24       MóV P                  '       d.   \        R.V P                   Uu. uF  pRV 2NK
  	  upO4       R	# \        WV4      ;'       g    \        V 4      pVe   \        R
RV 2.4       R	# R^R\        P                  ! R4      RV P                  RV P                  P!                  4       RV P"                   Uu. uF  qˆP!                  4       NK  	  up/p\%        V P
                  V4        \'        V P
                  R4       \        RV P
                  R,           R24       \)        V 4       \        R\+        V P                  W P
                  VR7       24       T P2                  p\5        T P
                  Y±4      pTe‚   T\7        T4      8:  dr   \	        T P
                  4      P9                  RR7       \        RR\;        T4       RRP=                  T4       2.T P>                   Uu. uF  pRT 2NK
  	  upO4       R# \A        T\7        T;'       g    . 4      ,
          4      p\        RRRP=                  T4       2R R\0         R!2.4       R	# u upi u upi   \,         dM   p	\        RT	 R2R.4       \/        T P
                  4      p
T
'       g   \        R\0         R24        Rp	?	R	# Rp	?	ii ; iu upi )"uh  Flip the flag, stop/uninstall every secondary gateway, bring up the multiplexer, verify.
Returns True when the multiplexer verifiably serves every profile.

Every step after the manifest write is fallible (a config write, a secondary's stop or its
unit's daemon-reload, a system unit that needs ``--run-as-user``, an unreachable user bus) and
runs inside ONE compensating boundary: on failure the manifest written before the first
destructive step brings ONE gateway back (:func:`rollback_migration`), so the host never ends
with nobody serving. The flag goes on first so an apply killed anywhere after it is resumable
from the manifest (flag on + manifest + no live multiplexer = interrupted).

**Resume beats preflight.** The manifest is read BEFORE the blocker gate: a manifest means an
earlier apply already removed units, so this run is a compensation, not an initiation, and a
gate that refuses it traps the host forever (no ``--standalone``, no rollback command). The
checks still run â€” as findings, see :func:`_resume_findings`. With no manifest nothing has been
destroyed yet and both gates refuse exactly as before.
u*   âœ“ Already multiplexed â€” nothing to do.TNu)     â†» resuming the migration recorded in u     âš  u    â€” resuming anywayu   âœ— Migration refused:u     â€¢ Fu/   âœ— Migration refused before changing anything:ÚversionÚmigrated_atz%Y-%m-%dT%H:%M:%S%zÚflag_wasr&   r„   u1     âœ“ default: gateway.multiplex_profiles: true (rM  rc   rp  r®   u     âœ— migration failed (uH     â†© Restoring one host gateway so this host is not left without one...z	  Re-run z to resume from the manifest.©Ú
missing_okrø  u-   âœ“ Migrated: the default gateway now serves z profiles: r   uJ   âš  Migration applied, but the default gateway has not confirmed serving: zX  Check `hermes gateway status` and the gateway log; the flag and manifest are in place.u5    once it is healthy â€” it resumes from the manifest.)!r’   rë  r  rx   r  rq   rí  rˆ  rl  r¢   r{   r~  r]  Ústrftimert   r&   rR   r�   rI  rR  rr  rh  râ   Úrollback_migrationr·   rž   rb  rB  Úunlinkr‘   rI   r|   r†  )r˜  r‰  r  r   rx   Úenable_failurerã  Úblockerrƒ   rÖ  Úrolled_backrY  ra  r  Úmissings   &$             r!   Úapply_migrationr˜  û  s,  € ð" ××ÐÜÐ:Ô;ÙÜ(¨Ó.Ñ€FØ�}‰}€HØÒô
 	Ð9¼.È×IZÑIZÓ:[Ð9\Ð]Ô^ÜÔ ¨kÓ:Ô;Ü0°Ó6ˆØÒ%Ü�F˜>Ð*Ð*>Ð?Ô@øà�<�<ˆ<ÜÐ,ÐUÀdÇmÂmÓ/TÁmÀ°&¸¸³ÁmÑ/TÐUÔVÙÜ" 4°Ó=×^Ð^ÔAXÐY]ÓA^ˆØÒÜÐEÈÐPWÈyÐGYÐZÔ[Ùà�q˜-¬¯ªÐ7LÓ)MØ˜×.Ñ.Ø�t—|‘|×+Ñ+Ó-Ø°×1LÒ1LÓMÑ1L¨AŸI™IžKÑ1LÑMð	
ˆô 	˜×)Ñ)¨8Ô4ðÜ˜d×/Ñ/°Ô6ÜÐAÀ$×BSÑBSÐVcÕBcÐAdÐdeÐfÔgÜ" 4Ô(Ü�Ô'¨¯©°f×>OÑ>OÐ]hÔiÐjÐkÔlð ×)Ñ)€HÜ˜d×/Ñ/°ÓG€FØÒ˜h¬#¨f«+Ô5ô 	�t×(Ñ(Ó)×0Ñ0¸DÐ0ÔAÜ�ÐCÄCÈÃKÀ=ÐP[Ð\`×\eÑ\eÐflÓ\mÐ[nÐoð 7Ø(,¯ªÓ5© 1�F˜1˜#“,©Ñ5ð7ô 	8áÜ�X¤ F§L L¨bÓ 1Õ1Ó2€GÜ
ˆBÐ\Ð]a×]fÑ]fÐgnÓ]oÐ\pÐqØfØœÐ(Ð(]Ð^ð`ô añ ùòU 0Uùò Nøô ô ÜÐ*¨3¨%¨qÐ1ØZð\ô 	]ä(¨×):Ñ):Ó;ˆßÜ�IœoÐ.Ð.KÐLÔMÝûðüò  6s,   Â9K,
ÅK1ÆA/K6 ÊM
Ë6MÌAMÍMg      >@c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   r)  rZ  r   r   r   )r    s   "r!   r"   r"   M  s!   € ÷ ñ  ð °ð ¸-ñ r$   c                óÆ   € \         P                  ! 4       V,           p \        V 4      pVe   V# \         P                  ! 4       V8¼  d   R# \         P                  ! R4       KF  )uU  Bounded wait for a gateway that is actually UP for ``home``; None on timeout.

A service-manager ``start`` that returns is not proof: systemd returns after its own start
timeout on a unit that keeps failing, and taking that for success is how the compensator
printed "âœ“ Restored" over a unit respawning every 5 s at ``ExecMainStatus=75``.
Nr\  )r]  r^  rã   r_  )r   r)  r`  r   s   &&  r!   Ú_wait_for_live_gatewayr›  M  sI   € ô �~Š~Ó 'Õ)€HØ
Ü Ó%ˆØŠ?ØˆJÜ�>Š>Ó˜xÔ'ÙÜ�
Š
�3Žr$   c               ó    € V ^8„  d   QhRRRR/# )r   rq   r   r   r   r   )r    s   "r!   r"   r"   ^  s   € ÷ Zñ Z ^ð Z¸tñ Zr$   c           
     óè  € T ;'       g    \        4       p \        V 4      pVf   \        R\        V 4       R24       R# R\        V 4       R\         R2p\        V4      pVf   \        R\        V 4       R	24       R#  \        V \        VP                  R
R4      4      4       \        R4       TP                  R4      p\        RT \        T \        4       4      \        T\        4      '       d   \        T4      M. ;'       g    \!        T 4      R7      pT Uu. uF  p\#        TR,          4      NK  	  pp \%        T \'        T4      4       \        R4       TP(                  '       gk   \*        ;QJ d    R T 4       F  '       g   K   RM	  RM! R T 4       4      '       g1   \        T 4      P-                  RR7       \        R\.         R24       R# \1        T4      w  rš \        R\3        YiY
R7       24       \7        T \8        4      pTf"   \        R\8        R R 24       \        T4       R# \        T 4      P-                  RR7       \        R!T R"24       T'       d&   \        R#R$P;                  T4       R%\         R&24       R#   \         d&   p\        RT R24       \        T4        Rp?R# Rp?ii ; iu upi   \         d&   p\        RT R24       \        T4        Rp?R# Rp?ii ; i  \         du   p\        RT R24        \5        T 4      pM)  \         d   p\        RT R24       Rp Rp?MRp?ii ; iT'       g   \        T4        Rp?R# \        R4        Rp?ELsRp?ii ; i)'u¿  COMPENSATOR for a failed apply: put ONE gateway back on this host.

Not a user-facing rollback and deliberately NOT a fleet restore. It used to reinstall and
start every recorded per-profile gateway, which the host gateway lock now forbids by
construction: this function clears the multiplex-owned served record first, so
``gateway.host_attach`` tells each secondary to "start normally" and the flock loser exits 75
into a supervisor respawn loop (observed live: a secondary won the race while the default's
unit sat at ``NRestarts=38, ExecMainStatus=75``, respawning every 5 s â€” ``StartLimitIntervalSec=0``
defeats the limiter). Under multiplex-only a restored fleet is not a state we want to be able
to reach, so the compensation is the one gateway the topology allows: the DEFAULT's, on the
recorded flag. The secondaries whose units this attempt already removed are named in the
output and are served by that gateway as soon as it multiplexes (``flag_was`` is either unset
or the retired ``false``; both resolve to multiplex at boot once nothing blocks it).

Returns True only when a gateway is VERIFIABLY live again; the manifest is kept otherwise so
the next ``hermes gateway migrate --multiplex`` resumes.
Nu   âœ— No migration manifest at z; nothing to restore.Fu.   âš  Compensation incomplete; manifest kept at z.
  Re-run u"    â€” it resumes from the manifest.u#   âœ— Malformed secondary records in z; fix or delete the manifest.rŽ  u2     âœ“ default: gateway.multiplex_profiles restoredu=     âœ— default: could not restore gateway.multiplex_profiles (rc   r&   )r   r   rP   u5     âœ“ default: cleared multiplex-owned runtime statusu?     âœ— default: could not clear multiplex-owned runtime status (c              3  óh   "  € T F(  qP                  R 4      ;'       g    \        V4      x € K*  	  R# 5i)r   N)rÈ   r$  )rC   r  s   & r!   rE   Ú%rollback_migration.<locals>.<genexpr>”  s+   é € Ð-oÑcnÐ\_¯g©g°e«n×.WÐ.WÔ@RÐSVÓ@WÔ.WÓcnùs   ‚2ž2Tr�  u-   âœ“ Nothing was running before this attempt (z); the flag is restored.rp  r®   uZ     âœ— default: could not bring the host gateway back up via the recorded service manager (z%); falling back to a detached gatewayu2     âœ— default: the detached fallback also failed (uE     âœ“ default: started the host gateway detached (no service manager)u=     âœ— default: no gateway confirmed serving this host within z.0fz5s (check `hermes gateway status` and the gateway log)u8   âœ“ Compensated: one host gateway is running again (pid z) on the recorded flag.z9  Per-profile gateways this attempt had already removed: r   u™   . They are NOT reinstalled â€” one gateway per host is the only supported topology â€” and the host gateway serves them as soon as it multiplexes.
  Run z to finish converging.)rÓ   rå  rë  r  r·   r  rR  r   rÈ   râ   r   rí   rè   r£  rN   r$  r  r   rW  rB  r5   r¸  r“  Ú_NOTHING_RECORDEDr/  rh  rA  r›  Ú_COMPENSATOR_WAIT_SECONDSrI   )rq   rx   Ú
incompleter„   rÖ  Údefault_recÚ
default_gwr  r‡  r  r   ÚspawnedÚ	spawn_excÚlives   &             r!   r’  r’  ^  sg  € ð$  ×2Ð2¤=£?€LÜ˜lÓ+€HØÒÜÐ-¬n¸\Ó.JÐ-KÐK`ÐaÔbÙØBÄ>ÐR^ÓC_ÐB`ð aÜ-Ð.Ð.PðR€Jä'¨Ó1€KØÒäÐ3´NÀ<Ó4PÐ3QÐQnÐoÔpÙðÜ˜l¬D°·±¸jÈ%Ó1PÓ,QÔRÜÐBÔCð —,‘,˜yÓ)€KÜØ�<Ô%5°lÔDWÓDYÓ%ZÜ5?ÀÌT×5RÒ5RÔ$ [Ô1ÐXZ÷  Að  AÔ_rÐsó  `Aô€Jñ -8Ó8©K SŒS��Y•Ö ©K€EÐ8ðÜ% l´C¸³JÔ?ÜÐEÔFð ×!×!Ð!¯#«#Ñ-oÑcnÓ-o¯#¯#ª#Ñ-oÑcnÓ-o×*oÒ*oÜ�|Ó$×+Ñ+°tÐ+Ô<ÜÐ=Ô>OÐ=PÐPhÐiÔjÙä/°Ó9Ñ€FðWÜ�Ô'¨
¸LÔbÐcÐdÔeô  " ,Ô0IÓJ€DØ‚|ÜÐMÔNgÐhkÐMlð mDð Eô 	FäˆjÔÙÜ�<Ó ×'Ñ'°4Ð'Ô8Ü	ÐDÀTÀFÐJaÐ
bÔcßÜÐIÈ$Ï)É)ÐTYÓJZÐI[ð \ô 'Ð'Ð'=ð?ô 	@ñ øôs ô ÜÐMÈcÈUÐRSÐTÔUÜˆjÔÝûðüò 9øô ô ÜÐOÐPSÈuÐTUÐVÔWÜˆjÔÝûðûô ô Wô 	ð Ø˜ÐCðEô 	Fð	Ü-¨lÓ;‰GøÜô 	ÜÐFÀyÀkÐQRÐSÔTØ�Gûð	ú÷ Ü�*ÔÝÜÐU×VÒVûðWús„   Á>0J ÄJ:Ä6 J? Ç K2 ÊJ7ÊJ2Ê2J7Ê?K/Ë
K*Ë*K/Ë2M1Ë=M,ÌLÌM,ÌL?Ì$L:Ì5M,Ì:L?Ì?
M,Í
M,ÍM,Í,M1c               ó   € V ^8„  d   QhRR/# r«   r   )r    s   "r!   r"   r"   ¾  s   € ÷ ñ  -ñ r$   c                 óä   € ^ RI Hp  V P                  4       '       g   R# ^ RIHp ^ RIHp V! 4       P                  pW1! R4      ,          P                  4       '       g   RW1! R4      ,           R2# R# )u  Reason the host cannot be converged by this command, else None.

Windows IS handled (per-profile Scheduled Tasks and the Startup-folder fallback are removed
like any other unit). s6 IS handled too, in-process: a named profile's slot that is UP is
parked (``s6-svc -d`` + ``down`` file) and its autostart intent folded into the root slot the
same way the container's boot does it (``hermes_cli.gateway_multiplex_s6``). What cannot be
done from here is register a slot the boot never created â€” that is the one refusal left.
rõ   N)r÷   rù   r&   z5s6-supervised container without a root gateway slot (u?   ); the container's boot registers it â€” restart the container.)	r  rö   r  r  r÷   r  rú   r  r	  )r  r÷   rú   r  s       r!   Ú_host_supports_migrationrª  ¾  sp   € õ )Ø×Ñ×!Ò!ÙÝ?Ý;ÙÓ ×(Ñ(€GØ�o iÓ0Õ0×8Ñ8×:Ò:ØGÈÐRaÐbkÓRlÕHlÐGmð nOð Oð 	Pár$   c               ó   € V ^8„  d   QhRR/# ©r   r   r!  r   )r    s   "r!   r"   r"   Ó  s   € ÷ 0ñ 0˜ñ 0r$   c                óò  € \        4       pV'       d%   \        RV 24       \        P                  ! ^4       \	        4       p\        V RR4      p\        \        W#R7      4       V'       d   R# VP                  '       d   R# VP                  fW   VP                  '       g   \        VP                  4      ^8  d+   \        P                  ! VP                  '       d   ^M^ 4       \        V RR4      '       gG   \        P                  P                  4       '       d#   ^ RIHp V! RR	4      '       g   \        R
4       R# \        4        \        P                  ! \#        V4      '       d	   ^ 4       R# ^4       R# )z=``hermes gateway migrate [--multiplex] [--dry-run] [--yes]``.u   âœ— rü  F©rü  NÚyes)Úprompt_yes_nozApply this migration now?TzAborted; nothing changed.)rª  rë  ÚsysÚexitrè  r0  rí  r  r’   rx   r¢   r‘   rs   ÚstdinÚisattyÚhermes_cli.setupr°  r˜  )ÚargsÚreasonr˜  rü  r°  s   &    r!   Úcmd_migrater¸  Ó  sø   € ä%Ó'€FßÜ��V�HˆoÔÜ�Š�ŒÜÓ!€DÜ�d˜I uÓ-€GÜ
Œ;�tÔ-Ô.ßÙØ××ÐÙð ‡}�}Ò $§,§, ,´#°d·m±mÓ2DÀqÔ2HÜ�Š�d—l—l�l‘¨Ô*ô �4˜ ×&Ò&¬3¯9©9×+;Ñ+;×+=Ò+=Ý2ÙÐ8¸$×?Ò?ÜÐ-Ô.ÙÜ	„GÜ‡H‚H”/ $×'Ò'ˆQÖ/¨QÖ/r$   c               ó   € V ^8„  d   QhRR/# r¬  r   )r    s   "r!   r"   r"   ð  s   € ÷ ñ ¨ñ r$   c                 óŠ  € ^ RI Hp Hp \        4       f   V! \	        4       4      '       d   R# \        4       pVP                  '       g   \        VP                  4      ^8  d   R# VP                  RJpVP                  '       g   V'       g   R# ^ RI Hp V'       d   RMV! VP                  4      pVe   \        RV R\         R24       R# \        4        V ! V4      pV'       g1   VP                  '       g	   V'       d   \!        \#        W&4      4       R# \        R4       \!        \%        VRR	7      4       \'        V4       R# )
a…  ``hermes update`` hook: with >= 2 profiles, per-profile gateways present and multiplex off,
migrate automatically when unblocked (deterministic, never prompts) or print the blocker block.
``gateway.auto_multiplex_migration: false`` on the default profile opts out; a secondary behind a
service-domain / UNIX-user / HERMES_HOME boundary blocks this path only (the explicit command decides).)rá  Úauto_migration_opted_outN)Úmasked_default_unituF   
  â„¹ Per-profile gateways kept: the default gateway unit is masked (z).
    Unmask it and run z to fold them into one gateway.uJ   â†’ Migrating per-profile gateways onto one multiplexed default gateway...Fr®  )rñ   rá  r»  rª  rÓ   rè  r’   r‘   rs   rx   r�   r¼  rq   rë  r·   r¢   rí  r3  r  r˜  )rá  r»  r˜  Úresumingr¼  Úmaskedr1  s          r!   Úmaybe_auto_migrate_after_updater¿  ð  sþ   € ÷
 dÜÓ!Ò-Ñ1IÌ-Ë/×1ZÒ1ZÙÜÓ!€DØ××Ð¤3 t§}¡}Ó#5¸Ô#9Ùð �}‰} DÐ(€HØ×&×&Ð&¯xÙÝEß‰TÑ#6°t×7HÑ7HÓ#I€FØÒÜÐWÐX^ÐW_ð `'Ü'6Ð&7Ð7VðXô 	YáÜ	„GÙ+¨DÓ1€Mß˜ŸŸ˜¯ÜÔ$ TÓ9Ô:ÙÜ	Ð
VÔWÜ
Œ;�t UÔ+Ô,Ü�DÖr$   r4   )^Ú__conditional_annotations__rX   Ú
__future__r   rà   r9  ÚloggingrÆ   r±  r]  Údataclassesr   r   Úpathlibr   Útypesr   Útypingr   r   r	   Ú	getLoggerrT   Úloggerr  r·   Ú_SERVED_WAIT_SECONDSr   rA   rl   rQ   rp   ÚcontextmanagerrÎ   rÓ   rÚ   rã   rè   rí   rò   r  r  r  r4  r7  r+  rA  rF  rR  r]  rc  rr  r|  rˆ  r’  r•  r›  r¿  rÌ  rÑ  rÒ  rY   r×  rè  rí  rú  r  r  r  r  r$  r'  r/  r   r3  r  rå  rç  rI  rW  rb  rh  rl  rr  r~  r  rˆ  r˜  r¡  r›  r’  rª  r¸  r¿  )rÀ  s   @r!   Ú<module>rË     s6  øðôõ* #ã Û Û Û 	Û 
Û ß (Ý Ý !ß /Ñ /à	×	Ò	˜8Ó	$€à(€Ø6€ØÐ ð ÷'
ð '
ó ð'
õTCõ9õ6ð ÷Y
ð Y
ó ðY
ð~ ×Ñô :ó ð :õF%õ
Hõ
òõõ&1õ
õ6õVð-Ð`d÷ -õD õ
õ,õ9õ	.õ%ð( ×Ñô'ó ð'õõ
õ<õõ.	/õRõ õFFõð8 !Ø!ðUÐ ÐQó õõ'õZõ
õ*/$õdõ*õõ4õ<õ
ð BÐ õõ(õ4õ2õ:õIõ(ðSð
 "&÷Sõ<õ(	Oõõ@5õ1ð*LÐAU÷ Lð^ !Ð õ÷"Zõ@õ*0÷:r$   