+
    jHàjF(  ã                  ó  a € 0 t $ R t^ RIHt ^ RIt^ RIt^ RIt^ RIHt ^ RI	H
t
HtHt ]
'       d	   ^ RIHtHt R R ltR R	 ltR
 R ltR R ltR R ltR R ltR R ltR R lt]]]3tR]R&   R R ltR R ltR R ltR# )a%  Boundaries the AUTOMATIC multiplex migration (``hermes update``) must not cross, and the opt-out.

The multiplexer replaces a kernel-enforced boundary (separate UNIX users, separate service domains,
separate HERMES_HOME trees) with in-process isolation. An operator may choose that with
``hermes gateway migrate --multiplex``; an unattended update hook must not choose it for them.
``build_migration_plan`` records the same findings as NOTICES so a dry run shows them; only
:func:`maybe_auto_migrate_after_update` treats them as blockers (#109954).
)ÚannotationsN)ÚPath)ÚTYPE_CHECKINGÚCallableÚOptional)ÚMigrationPlanÚProfileGatewayc               ó    € V ^8„  d   QhRRRR/# )é   ÚpidÚintÚreturnúOptional[int]© )Úformats   "ÚF/home/ubuntu/.hermes/hermes-agent/hermes_cli/gateway_migrate_guards.pyÚ__annotate__r      s   € ÷ ñ �#ð ˜-ñ ó    c                ó¬  € \         P                  ! \        4      ;_uu_ 4        \        P                  ! RV  24      P
                  uuRRR4       #   + '       g   i     M; i^ RIHp T! 4       '       g   R# \         P                  ! \        \        \        P                  4      ;_uu_ 4        \        P                  ! RRRR\        T 4      .RRR	R
^R7      pTP                  ^ 8X  dN   TP                  P                  4       '       d.   \!        TP                  P                  4       4      uuRRR4       # RRR4       R#   + '       g   i     R# ; i)z[Owner uid of a live process: ``/proc`` where it exists, ``ps`` on macOS; None when unknown.z/proc/N)Úis_macosÚpsz-ozuid=z-pTzutf-8F)Úcapture_outputÚtextÚencodingÚcheckÚtimeout)Ú
contextlibÚsuppressÚOSErrorÚosÚstatÚst_uidÚhermes_cli.gatewayr   Ú
ValueErrorÚ
subprocessÚSubprocessErrorÚrunÚstrÚ
returncodeÚstdoutÚstripr   )r   r   Úresults   &  r   Ú_pid_uidr,      sä   € ä	×	Ò	œW×	%Õ	%Ü�wŠw˜ ˜u�~Ó&×-Ñ-÷ 
&×	%×	%Ò	%úå+Ù�:Š:ÙÜ	×	Ò	œW¤j´*×2LÑ2L×	MÕ	MÜ—’  t¨V°T¼3¸s»8Ð DÐUYÐ`dÐovØ&+°Qô8ˆà×Ñ Ô! f§m¡m×&9Ñ&9×&;Ò&;Ü�v—}‘}×*Ñ*Ó,Ó-÷	 
N×	MÒ	Mñ
 ÷ 
NÖ	Mñ
 ús   £#AÁA!	Â/AEÄ"EÅE	c               ó    € V ^8„  d   QhRRRR/# )r
   r   r   r   Úboolr   )r   s   "r   r   r   (   s   € ÷ 
1ñ 
1˜Cð 
1 Dñ 
1r   c                ó¦   € \        \        R4      '       d   \        P                  ! 4       M^ pV'       d   \        V 4      MRpVRJ;'       d    W!8g  # )u¤  True when *pid* is owned by another Unix user than this non-root caller.

Gateway process scans match argv host-wide, so without this a second account's gateway (users
isolate credentials by UID) became a ``restart --all`` SIGTERM target and "unfolded profile"
advice. Root keeps the host-wide view (``sudo â€¦ --system`` over a ``User=`` unit), as does
Windows (no ``geteuid``); an unknown owner is not called foreign.
ÚgeteuidN)Úhasattrr   r0   r,   )r   Úmy_uidÚowners   &  r   Úpid_is_other_usersr4   (   s?   € ô %¤R¨×3Ò3ŒR�ZŠZŒ\¸€Fß#ŒH�SŒM¨€EØ˜Ð×0Ð0 ¡Ð0r   c               ó    € V ^8„  d   QhRRRR/# )r
   Ú	unit_pathr   r   r   r   )r   s   "r   r   r   5   s   € ÷ 	ñ 	 ð 	¨ñ 	r   c                óê   € ^ RI Hp V! V 4      pVf   ^ # ^ RIp\        P                  ! \
        4      ;_uu_ 4        VP                  V4      P                  uuRRR4       #   + '       g   i     R# ; i)z[uid a system unit runs as: its ``User=`` (root when absent); None when the name is unknown.)Ú_read_systemd_user_from_unitN)r"   r8   Úpwdr   r   ÚKeyErrorÚgetpwnamÚpw_uid)r6   r8   Úuserr9   s   &   r   Ú_system_unit_uidr>   5   sO   € å?Ù'¨	Ó2€DØ‚|ÙÛÜ	×	Ò	œX×	&Õ	&Ø�|‰|˜DÓ!×(Ñ(÷ 
'×	&×	&Ñ	&áús   »A!Á!A2	c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r
   Úhomer   r   r   Úserviceszlist[tuple[str, bool]]r   ztuple[Optional[int], Path]r   )r   s   "r   r   r   A   s*   € ÷ ñ ˜4ð  mð Ð?Uð ÐZtñ r   c                óJ  € ^ RI HpHp ^ RIHp Ve   \        V4      MRpT pRpV F|  w  ršV	R8w  d   K  V! V 4      ;_uu_ 4        V! V
R7      pRRR4       V! X4      pV'       d    Wp8X  d   \        V4      P                  4       pV
'       g   Ki  RpVe   Kq  \        V4      pK~  	  VfQ   V'       gI   \        P                  ! \        4      ;_uu_ 4        V P                  4       P                  pRRR4       Wg3# Wg3#   + '       g   i     L·; i  + '       g   i     Yg3# ; i)u†  ``(uid, runtime_home)`` of the gateway that serves ``home``.

uid: the live process owner; else, for a system unit, its ``User=`` â€” and ONLY that: a system
unit's principal is whatever systemd will run, so an unresolvable ``User=`` stays None rather than
borrowing the profile directory's owner (a stopped unit pinned to an absent NSS user is not the
account that owns the files). Without a system unit (user-scope systemd / launchd / detached), the
profile directory owner is the account the gateway runs as. None means unknown. runtime_home: the
HERMES_HOME an installed unit pins, which is where the gateway really runs; ``home`` otherwise.
)Ú_hermes_home_pinned_by_unitÚget_systemd_unit_path©Ú	_home_envNFÚsystemd©ÚsystemT)r"   rC   rD   Úhermes_cli.gateway_migraterF   r,   r   Ú
expanduserr>   r   r   r   r    r!   )r@   r   rA   rC   rD   rF   ÚuidÚruntime_homeÚhas_system_unitÚkindrI   r6   Úpinneds   &&&          r   Úgateway_identityrQ   A   së   € ÷ VÝ4à*-ª/œ #œ¸t€CØ€LØ€OÛ ‰ˆØ�9ÔÙÙ�t�_�_Ù-°VÔ<ˆI÷ á,¨YÓ7ˆß�lÔ*Ü ›<×2Ñ2Ó4ˆLß‰6Ø"ˆOØŒ{Ü& yÓ1’ñ !ð ‚{Ÿ?Ü× Ò ¤×)Õ)Ø—)‘)“+×$Ñ$ˆC÷ *àÐÐˆ3ÐÐ÷ �_ú÷ *Ö)àÐÐús   Á
C=ÃDÃ=DÄD"	c               ó    € V ^8„  d   QhRRRR/# )r
   Úprofiler   r   r'   r   )r   s   "r   r   r   f   s   € ÷ \ñ \˜Nð \¨sñ \r   c                óJ   € V P                   '       d   V P                  4       # R # )zno service manager (detached))rA   Úservice_label)rS   s   &r   Ú_service_labelrV   f   s"   € Ø&-×&6×&6Ð&6ˆ7× Ñ Ó"Ð[Ð<[Ð[r   c               ó$   € V ^8„  d   QhRRRRRR/# ©r
   Úplanr   rS   r   r   zOptional[str]r   )r   s   "r   r   r   j   s&   € ÷ Hñ H ð H¸ð HÈ=ñ Hr   c                óø  € \        VP                  4      ^8”  d"   RVP                   RVP                  4        R2# V P	                  4       pV P
                  P                  ;'       g
    Ve   V.M. p\        VP                  4      \        V4      8X  d   R# ^ RIHp V P
                  P                  '       d   R\        V P
                  4       2M
RV! V4       2pRVP                   R\        V4       R	V R
2# )u–  Different manager or scope than the one the fleet converges on (system vs user systemd, launchd vs
systemd). The reference is the default's own unit when it has one, else the manager
``target_service_kind()`` elects from the secondaries: a default that never had a gateway unit is not a
service domain of its own, and refusing every secondary against it left the common upgrade fleet (N
launchd profiles, unit-less default, #118097) printing blockers instead of folding. Two managers
among the secondaries still refuse â€” the ones not elected differ from the target. Two units on one
profile is an ambiguous topology the unattended path does not resolve either.ú	Profile 'z'' has more than one installed service (z=): an ambiguous service topology is not folded automatically.N)rV   zthe default gateway runs under zthe fleet converges on z' runs under z while z9: a different service domain is not folded automatically.)	ÚlenrA   ÚnamerU   Útarget_service_kindÚdefaultÚsetrJ   rV   )rY   rS   ÚtargetÚ	referenceÚ_kind_labelÚagainsts   &&    r   Ú_guard_service_domainre   j   sÿ   € ô ˆ7×ÑÓ˜qÔ Ø˜GŸL™L˜>Ð)PÐQX×QfÑQfÓQhÐPið jMð Nð 	Oà×%Ñ%Ó'€FØ—‘×%Ñ%×QÐQ°fÒ6H¨6©(Èb€IÜ
ˆ7×ÑÓ¤ I£Ô.ÙÝHØSW×S_ÑS_×Sh×ShÐShÐ0´ÀÇÁÓ1MÐ0NÑOØ-©k¸&Ó.AÐ-BÐCð à˜Ÿ™�~ ]´>À'Ó3JÐ2KÈ7ÐSZÐR[ð \Fð Gð Hr   c               ó$   € V ^8„  d   QhRRRRRR/# rX   r   )r   s   "r   r   r   €   s&   € ÷ Vñ V˜=ð V°>ð VÀmñ Vr   c                ó\  € V P                   P                  pVf   V P                   P                  '       d   R# VP                  f#   VP                  '       d   RVP                   R2# Ve    VP                  e   VP                  V8X  d   R # RVP                   RVP                   RV R2# )Nz’The default gateway runs a system unit whose User= cannot be resolved on this host: an unknown service principal is not folded into automatically.r[   z{' runs a system unit whose User= cannot be resolved on this host: an unknown service principal is not folded automatically.z' runs as uid z' while the default gateway runs as uid z8: a UNIX privilege boundary is not folded automatically.)r_   rL   rN   r]   )rY   rS   Údefault_uids   && r   Ú_guard_unix_userri   €   s§   € Ø—,‘,×"Ñ"€KØÒ˜tŸ|™|×;×;Ð;ñQà‡{�{Ò˜w×6×6Ð6à˜GŸL™L˜>ð *Lð Mð 	NàÒ˜gŸk™kÒ1°W·[±[ÀKÔ5OÙØ˜Ÿ™�~ ^°G·K±K°=Ð@gØˆmÐSðUð Vr   c               ó$   € V ^8„  d   QhRRRRRR/# rX   r   )r   s   "r   r   r   ‘   s&   € ÷ Uñ U˜=ð U°>ð UÀmñ Ur   c           	     ó  € V P                   R ,          P                  4       pVP                  ;'       g    VP                  P                  4       pVP	                  V4      '       d   R# RVP
                   RV RV RVP                   R2	# )ÚprofilesNr[   z' runs with HERMES_HOME=z
, outside z: the multiplexer would serve z instead of the live home.)Údefault_homeÚresolverM   r@   Úis_relative_tor]   )rY   rS   Úprofiles_rootrM   s   &&  r   Ú_guard_home_treerq   ‘   s‡   € Ø×&Ñ&¨Õ3×<Ñ<Ó>€MØ×(Ñ(×8Ð8¨G¯L©L×AÑAÓC€LØ×"Ñ" =×1Ò1ÙØ˜Ÿ™�~Ð%=¸l¸^È:ÐVcÐUdð e+Ø+2¯<©<¨.Ð8RðTð Ur   zDtuple[Callable[[MigrationPlan, ProfileGateway], Optional[str]], ...]Ú_AUTO_MIGRATION_GUARDSc               ó    € V ^8„  d   QhRRRR/# )r
   rY   r   r   z	list[str]r   )r   s   "r   r   r   ¡   s   € ÷ 	)ñ 	) -ð 	)°Iñ 	)r   c           	     óº   € V P                    UUu. uF   p\         F  pV! W4      ;pf   K  VNK  	  K"  	  ppp\        \        P	                  V4      4      # u uppi )uµ   Every boundary a standalone secondary sits behind; empty when the fleet is one user, one service
domain, one profiles/ tree â€” the only shape ``hermes update`` may fold on its own.)Ústandalone_secondariesrr   ÚlistÚdictÚfromkeys)rY   rS   ÚguardÚfindingÚfindingss   &    r   Úauto_migration_blockersr|   ¡   sa   € ð
 ×2Ò2ôá2ˆGß+ˆEÙ˜TÓ+Ð+ˆG÷ 	ˆá+ñ 	Ù2ð ñ ô ”—‘˜hÓ'Ó(Ð(ùós
   �A¬
Ac               ó    € V ^8„  d   QhRRRR/# )r
   rm   r   r   zOptional[Path]r   )r   s   "r   r   r   °   s   € ÷ ñ  dð ¨~ñ r   c           	     óü  € ^ RI Hp ^ RIHp V! V 4      ;_uu_ 4        R Uu. uF  q1P	                  VR7      NK  	  ppRRR4       X F„  p\
        P                  ! \        4      ;_uu_ 4        VP                  4       '       dA   \        P                  P                  V4      \        P                  8X  d   VuuRRR4       u # RRR4       K†  	  R# u upi   + '       g   i     L¡; i  + '       g   i     K³  ; i)a  The default profile's systemd unit when the operator MASKED it (``systemctl mask`` links it to
``/dev/null``), else None. A masked survivor cannot be enabled at boot, and masking is a deliberate
"never start this", so the update hook reads it as an opt-out instead of refusing (#139324).)ÚgatewayrE   rH   N)FT)Ú
hermes_clir   rJ   rF   rD   r   r   r   Ú
is_symlinkr   ÚpathÚrealpathÚdevnull)rm   ÚgwrF   rI   ÚunitsÚunits   &     r   Úmasked_default_unitrˆ   °   sª   € õ )Ý4Ù	�<×	 Õ	 ÙGTÓUÁ}¸V×)Ñ)°Ð)Ö8Á}ˆÐU÷ 
!ãˆÜ× Ò ¤×)Õ)Ø�‰× Ò ¤R§W¡W×%5Ñ%5°dÓ%;¼r¿z¹zÔ%IØ÷ *×)Ö)ñ ñ ùò V÷ 
!×	 ú÷ *×)Ð)ús(   �C¢C¼CÁ-A
C*ÃCÃC'	Ã*C;c               ó    € V ^8„  d   QhRRRR/# )r
   rm   r   r   r.   r   )r   s   "r   r   r   ¿   s   € ÷ ^ñ ^¨4ð ^°Dñ ^r   c                ó  € ^ RI Hp ^ RIHp ^ RIHp V! V 4      ;_uu_ 4        V! 4       P                  R4      pRRR4       \        X\        4      '       g   R# V! VP                  R4      RR	7      '       * #   + '       g   i     LF; i)
at  ``gateway.auto_multiplex_migration: false`` in the DEFAULT profile's EFFECTIVE config: the same
``load_config`` the rest of the CLI reads (``DEFAULT_CONFIG`` + config.yaml + the managed overlay), so
an administrator's managed ``false`` wins over a user's ``true`` and a YAML string ``"false"`` is
false, not truthy. Only the nested key counts, there is no top-level alias.)Úload_config_readonlyrE   )Úis_truthy_valuer   NFÚauto_multiplex_migrationT)r_   )	Úhermes_cli.configr‹   rJ   rF   ÚutilsrŒ   ÚgetÚ
isinstancerw   )rm   r‹   rF   rŒ   Úgateway_sections   &    r   Úauto_migration_opted_outr“   ¿   sf   € õ
 7Ý4Ý%Ù	�<×	 Õ	 Ù.Ó0×4Ñ4°YÓ?ˆ÷ 
!ä�o¤t×,Ò,ÙÙ˜×2Ñ2Ð3MÓNÐX\Ô]Ô]Ð]÷	 
!×	 ús   £A8Á8B	)Ú__conditional_annotations__Ú__doc__Ú
__future__r   r   r   r$   Úpathlibr   Útypingr   r   r   rJ   r   r   r,   r4   r>   rQ   rV   re   ri   rq   rr   Ú__annotations__r|   rˆ   r“   )r”   s   @r   Ú<module>rš      s�   øðôõ #ã Û 	Û Ý ß 4Ñ 4çßHõõ
1õ	õõJ\õHõ,Võ"Uð ØØð`Ð Ð\ó õ	)õ÷^r   