+
    iHàj¾  ã                  ó¦  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
t
^ RI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HtHt ^ RIHt ^ RIHtHtHtH t  ^ RI!H"t"H#t# ]PH                  ! ]%4      t&^t'^t(Rt)^t*^t+R	t,R®t-R
].R&   RR¯RR°RR±RR²RR³RR´RRµRR¶/t/R].R&   RRR·RR¸RR¹/RRRºRR»RR¹/RRR¼RR½RR¾//t0R].R&   R¿t1R
].R&   RÀt2R
].R&   RÁt3R
].R &   RÂt4R
].R!&   ]5! ]R"]Pl                  4      t7]5! ]R#^ 4      t8/ t9R$].R%&   R&t:Rs;R'].R(&   ] ! R) R*4      4       t<] ! R+ R,4      4       t=R- R. lt>R/ R0 lt?R1R2/R3 R4 llt@R5R2/R6 R7 lltAR8 R9 ltBR: R; ltCR<R2/R= R> lltDR? R@ ltERA RB ltFRC RD ltGR5R2/RE RF lltHRÃRG RH lltIRI RJ ltJRK RL ltKR5R2/RM RN lltLRO tMRP RQ ltNRRR/RS RT lltORÄRU RV lltPRW RX ltQRYRZR[R\/tRR] R^ ltSR_ R` ltTRa Rb ltURc],RdRReRRfRRgR/Rh Ri lltVRj Rk ltWRl Rm ltXRn Ro ltYRp Rq ltZRr Rs lt[RtR/Ru Rv llt\RtR/Rw Rx llt]RyR2/Rz R{ llt^R| R} lt_R~ R lt`R€ R� ltaR‚ Rƒ ltbR„ R… ltcR† R‡ ltdRˆRRRRR‰RR1RŠR‹R2RŒR/R� RŽ llte]R� 4       tfR� R‘ ltgR’ R“ lthR” R• ltiR–^/R— R˜ lltjR‰RR™R2RŒR/Rš R› lltkRœR/R� Rž lltlRŸ R  ltmR¡ R¢ ltnR£ R¤ ltoR¥ R¦ ltpR§ R¨ ltqR©^/Rª R« lltrR¬ R­ lts. RÅOttR# )Æu  iron-proxy (``ironsh/iron-proxy``) integration for credential-injecting egress control.

Sandboxes (Docker/Modal/SSH) hold only opaque proxy tokens; iron-proxy â€” a TLS-intercepting,
default-deny egress firewall â€” swaps them for real credentials on the way out, so a leaked
token is useless outside the trusted proxy boundary.  The pinned binary is auto-installed
into the PM tool store; CA, ``proxy.yaml``, ``mappings.json``, pidfile and logs live in
``<hermes_home>/proxy``.  Failures warn and never block agent startup.
)ÚannotationsN)ÚcontextmanagerÚsuppress)Ú	dataclassÚfieldÚreplace)ÚPath)ÚDictÚListÚOptionalÚTuple)Úatomic_json_writeÚatomic_write_textÚHERMES_IRON_PROXY_MGMT_KEYi‚#  útuple[str, ...]Ú_DEFAULT_ALLOWED_HOSTSÚOPENROUTER_API_KEYÚOPENAI_API_KEYÚGROQ_API_KEYÚTOGETHER_API_KEYÚDEEPSEEK_API_KEYÚMISTRAL_API_KEYÚXAI_API_KEYÚNOUS_API_KEYzdict[str, tuple[str, ...]]Ú_BEARER_PROVIDERSÚANTHROPIC_API_KEYÚhostsÚmatch_headersÚaliasesÚAZURE_OPENAI_API_KEYÚGEMINI_API_KEYz%dict[str, dict[str, tuple[str, ...]]]Ú_HEADER_AUTH_PROVIDERSÚ_NON_BEARER_PROVIDERSÚ_DEFAULT_UPSTREAM_DENY_CIDRSÚ_PROXY_SUBPROCESS_ENV_ALLOWLISTÚ_PROXY_SUBPROCESS_ENV_STRIPÚSIGKILLÚ
O_NOFOLLOWúdict[str, str]Ú_VERSION_CACHEÚHERMES_IRON_PROXY_NONCEúOptional[str]Ú_proxy_noncec                  óÐ   € ] tR t^ut$ RtR]R&   RtR]R&   RtR]R&   RtR]R	&   Rt	R]R
&   Rt
R]R&   RtR]R&   ]tR]R&   ]! ]R7      tR]R&   ]R R l4       t]R R l4       tRtR# )ÚProxyStatusFÚboolÚenabledNúOptional[Path]Úbinary_pathr+   Úbinary_versionÚconfig_pathÚca_cert_pathúOptional[int]ÚpidÚ	listeningÚintÚtunnel_port)Údefault_factoryú	list[str]Úwarningsc               ó   € V ^8„  d   QhRR/# ©é   Úreturnr/   © )Úformats   "ÚC/home/ubuntu/.hermes/hermes-agent/agent/proxy_sources/iron_proxy.pyÚ__annotate__ÚProxyStatus.__annotate__‚   s   € ÷ Jñ J˜4ñ Jó    c                	ób   € V P                   R J;'       d    V P                   P                  4       # ©N)r2   Úexists©Úselfs   &rD   Ú	installedÚProxyStatus.installed�   s*   € à×Ñ tÐ+×IÐI°×0@Ñ0@×0GÑ0GÓ0IÐIrG   c               ó   € V ^8„  d   QhRR/# r?   rB   )rC   s   "rD   rE   rF   †   s   € ÷ yñ y˜Dñ yrG   c                	óÜ   € \        V P                  ;'       dQ    V P                  P                  4       ;'       d/    V P                  ;'       d    V P                  P                  4       4      # rI   )r/   r4   rJ   r5   rK   s   &rD   Ú
configuredÚProxyStatus.configured…   sP   € ä�D×$Ñ$×wÐw¨×)9Ñ)9×)@Ñ)@Ó)B×wÐwÀt×GXÑGX×wÐwÐ]a×]nÑ]n×]uÑ]uÓ]wÓxÐxrG   rB   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__r0   Ú__annotations__r2   r3   r4   r5   r7   r8   Ú_DEFAULT_TUNNEL_PORTr:   r   Úlistr=   ÚpropertyrM   rQ   Ú__static_attributes__rB   rG   rD   r.   r.   u   s‹   ‡ à€GˆTÓØ"&€K�Ó&Ø$(€N�MÓ(Ø"&€K�Ó&Ø#'€L�.Ó'Ø€CˆÓØ€IˆtÓØ+€K�Ó+Ù°Ô5€HˆiÓ5àôJó ðJð ôyó ôyrG   r.   c                  óV   € ] tR t^Št$ RtR]R&   R]R&   R]R&   RtR]R&   R	tR]R&   R	tR
# )ÚTokenMappingzºSandbox-visible proxy token -> upstream credential lookup.  ``real_env_name`` is read from iron-proxy's
OWN env at egress; ``alias_env_names`` are extra SANDBOX names for the same token.ÚstrÚproxy_tokenÚreal_env_namer   Úupstream_hostsr   Úalias_env_namesrB   N©ÚAuthorization)	rS   rT   rU   rV   Ú__doc__rW   r   rb   r[   rB   rG   rD   r]   r]   Š   s.   ‡ ñZàÓØÓØ#Ó#Ø%7€M�?Ó7Ø')€O�_×)rG   r]   c               ó   € V ^8„  d   QhRR/# ©r@   rA   r   rB   )rC   s   "rD   rE   rE   •   s   € ÷ 'ñ '˜Tñ 'rG   c                 ó*   € ^ RI Hp  V ! 4       R,          # )é    ©Úget_hermes_homeÚproxy)Úhermes_constantsrk   rj   s    rD   Ú_proxy_state_dir_rorn   •   s   € Ý0ÙÓ˜wÕ&Ð&rG   c               ó   € V ^8„  d   QhRR/# rg   rB   )rC   s   "rD   rE   rE   š   s   € ÷ ñ ˜$ñ rG   c                 óÊ   € \        4       ;p P                  RRR7       \        \        4      ;_uu_ 4        V P	                  R4       RRR4       V #   + '       g   i     T # ; i)ztProxy state dir (CA key, pidfile, logs), created 0o700; unconditional chmod tightens a pre-existing slack-umask dir.T)ÚparentsÚexist_okiÀ  N)rn   Úmkdirr   ÚOSErrorÚchmod)Úds    rD   Ú_proxy_state_dirrw   š   sK   € ä
Ó
Ð€Q×&Ñ&¨t¸dÐ&ÔCÜ	”'×	Õ	Ø	�‰�Œ÷ 
à€H÷ 
Ö	à€Hús   µAÁA"	Úinstall_if_missingFc               ó    € V ^8„  d   QhRRRR/# )r@   rx   r/   rA   r1   rB   )rC   s   "rD   rE   rE   ¢   s   € ÷ ñ ¨4ð ¸Nñ rG   c                ój  € \         P                  ! R4      ;p'       d   \        V4      # ^ RIpVP	                  R4      pVe   VP
                  # V '       d.    VP                  R4       VP	                  R4      P
                  # R#   \         d"   p\        P                  RT4        Rp?R# Rp?ii ; i)zJExternal tools do not require PM platform support; acquire only on a miss.ú
iron-proxyNz"iron-proxy auto-install failed: %s)
ÚshutilÚwhichr   ÚpmÚinstalled_packageÚbinaryÚensureÚ	ExceptionÚloggerÚwarning)rx   Úsystemr~   ÚselectedÚexcs   $    rD   Úfind_iron_proxyrˆ   ¢   s�   € ä—’˜lÓ+Ð+€vÖ+Ü�F‹|ÐÛà×#Ñ# LÓ1€HØÒØ�‰Ðßð	FØ�I‰I�lÔ#Ø×'Ñ'¨Ó5×<Ñ<Ð<ñ øô ô 	FÜ�N‰NÐ?À×EÐEÙûð	Fús   Á+B ÂB2ÂB-Â-B2Úforcec               ó    € V ^8„  d   QhRRRR/# )r@   r‰   r/   rA   r   rB   )rC   s   "rD   rE   rE   ´   s   € ÷ ñ  ð °$ñ rG   c                ó¨   € ^ RI pVP                  RRR7       VP                  R4      P                  p\        P                  \        V4      R4       V# )zDExplicit setup/repair; PM verifies and repairs existing entries too.Nr{   T)Úexplicit)r~   r�   r   r€   r)   Úpopr^   )r‰   r~   Útargets   $  rD   Úinstall_iron_proxyr�   ´   sD   € ãà‡I�Iˆl T€IÔ*Ø×!Ñ! ,Ó/×6Ñ6€FÜ×Ñ”s˜6“{ DÔ)Ø€MrG   c               ó$   € V ^8„  d   QhRRRRRR/# )r@   Útmpr   Úchecksum_pathrA   r/   rB   )rC   s   "rD   rE   rE   ¾   s!   € ÷ ñ  Tð ¸$ð À4ñ rG   c           	     óv  € \         P                  ! R4      ;p'       g   \        P                  R4       R# V R,          V R,          rCVP	                  4       '       d   VP	                  4       '       g   \        P                  R4       R# \
        P                  ! RR7      ;_uu_ 4       pVR	VR
R.p\        . VORN\        V4      N^<R7      ;pP                  ^ 8w  dC   \        P                  RVP                  P                  RR4      R,          4        RRR4       R# \        . VORN\        V4      N\        V4      N^<R7      ;pP                  ^ 8w  d0   \        RVP                  P                  RR4      R,           24      h\        P                  R4        RRR4       R#   + '       g   i     R# ; i)uñ   Best-effort GPG check of checksums.txt in an ephemeral keyring.  False (with a warning) when gpg or the signature
assets are unavailable â€” SHA-256 stays enforced, gpg is never a hard dependency.  Raises ONLY on a present-but-bad signature.Úgpguu   gpg not found on PATH â€” skipping iron-proxy release-signature verification (SHA-256 checksum check still enforced).Fzchecksums.txt.asczpublic-key.ascuv   iron-proxy release signature assets unavailable â€” skipping GPG verification (SHA-256 checksum check still enforced).zhermes-iron-signature-©Úprefixz	--homedirz--batchz--no-ttyz--import©Útimeoutub   Could not import iron-proxy signing key â€” skipping GPG verification (SHA-256 still enforced): %súutf-8r   :NéÈ   NNz--verifyu~   iron-proxy checksums.txt failed GPG signature verification â€” refusing to install (possible release-channel tampering). gpg: :Ni,  Nz0Verified iron-proxy checksums.txt GPG signature.T)r|   r}   rƒ   r„   Úis_fileÚtempfileÚTemporaryDirectoryÚ_runr^   Ú
returncodeÚstderrÚdecodeÚRuntimeErrorÚinfo)	r‘   r’   r”   Úsig_pathÚpubkey_pathÚ
gnupg_homeÚgpg_baseÚimpÚverifys	   &&       rD   Ú_verify_checksums_signaturerª   ¾   sî  € ô —<’< Ó&Ð&ˆCÖ&Ü�‰ð  Oô  	PÙØÐ"5Õ5°sÐ=MÕ7MˆkØ×Ñ×Ò [×%8Ñ%8×%:Ò%:Ü�‰ð  Pô  	QÙÜ	×	$Ò	$Ð,D×	EÕ	EÈØ˜ j°)¸ZÐHˆÜÐA˜(ÐA JÐA´°KÓ0@ÐAÈ2ÔNÐNˆC×ZÑZÐ^_Ô_Ü�N‰NÐð  BE÷  BLñ  BL÷  BSñ  BSð  T[ð  ]fó  Bgð  hlõ  Bmô  nØ÷	 
FÑ	Eô
 ÐU˜XÐU zÐU´3°x³=ÐUÄ#ÀmÓBTÐUÐ_aÔbÐbˆF×nÑnÐrsÔsÜð Qð  RX÷  R_ñ  R_÷  Rfñ  Rfð  gnð  pyó  Rzð  {õ  R@ð  QAð  Bóð ô 	�‰ÐFÔGØ÷ 
F×	E×	EÒ	Eús   Â)A-F'Ä A=F'Æ'F8	c               ó   € V ^8„  d   QhRR/# )r@   rA   r(   rB   )rC   s   "rD   rE   rE   Õ   s   € ÷ Zñ Z˜ñ ZrG   c                 ó�   € \          U u/ uF1  q \        P                  9   g   K  V \        P                  V ,          bK3  	  up # u up i )uN   Infrastructure-only env (PATH, HOME, locale) â€” never the operator's secrets.)r$   ÚosÚenviron)Úns    rD   Úallowlisted_envr°   Õ   s4   € å&EÓYÑ&E ÌbÏjÉjÉÔˆAŒr�z‰z˜!�}ÒÑ&EÑYÐYùÒYs
   ‰A¤AÚtextc               ó(   € V ^8„  d   QhRRRRRRRR/# )	r@   Úargvr<   r˜   r9   r±   r/   rA   zsubprocess.CompletedProcessrB   )rC   s   "rD   rE   rE   Ú   s/   € ÷ jñ jˆyð j cð j°ð jÐLgñ jrG   c          	     ó�   € V'       d   VP                  R RRR7       \        P                  ! V 3RR RVR\        P                  /VB # )Tr™   r   )r±   ÚencodingÚerrorsÚcapture_outputr˜   Ústdin)ÚupdateÚ
subprocessÚrunÚDEVNULL)r³   r˜   r±   Úkwargss   &$$,rD   rž   rž   Ú   sA   € ßØ�‰˜4¨'¸)ˆÔDÜ�>Š>˜$Ñi¨tÐi¸WÐiÌJ×L^ÑL^ÐiÐbhÑiÐirG   c               ó    € V ^8„  d   QhRRRR/# )r@   r€   r   rA   r^   rB   )rC   s   "rD   rE   rE   à   s   € ÷ ñ ˜tð ¨ñ rG   c                óp  € \        V 4      ;p\        9   d   \        V,          #  \        \        V 4      R.\        R\	        4       R7      pTP                  ;'       g    TP                  ;'       g    RP                  4       ;p'       d
   T\        T&   T#   \
        \        P                  3 d     R# i ; i)zP``iron-proxy --version`` output, stripped and cached by path.  Empty on failure.z	--versionT)r˜   r±   ÚenvÚ )r^   r)   rž   Ú_RUN_TIMEOUTr°   rt   rº   ÚTimeoutExpiredÚstdoutr    Ústrip)r€   ÚkeyÚresÚouts   &   rD   Úiron_proxy_versionrÉ   à   sœ   € ä�6‹{Ðˆœ~Ô-Ü˜cÕ"Ð"ðä”C˜“K Ð-´|È$ÔTcÓTeÔfˆð �z‰z×-Ð-˜SŸZ™Z×-Ð-¨2×4Ñ4Ó6Ð6€sÖ6Ø!Œ�sÑØ€Jøô	 ”Z×.Ñ.Ð/ô Úðús   ¥&B ÂB5Â4B5c               ó$   € V ^8„  d   QhRRRRRR/# )r@   Úpathr   ÚdataÚbytesrA   ÚNonerB   )rC   s   "rD   rE   rE   î   s!   € ÷ 	ñ 	˜dð 	¨%ð 	°Dñ 	rG   c                óü  € \         P                  ! \        V 4      \         P                  \         P                  ,          \         P
                  ,          \        ,          R4      p \        \        \        4      ;_uu_ 4        \         P                  ! VR4       RRR4       \         P                  ! W!4       \         P                  ! V4       R#   + '       g   i     L>; i  \         P                  ! T4       i ; i)z˜Create/truncate ``path`` 0o600 from the first byte (no chmod-after TOCTOU), O_NOFOLLOW against a planted
symlink, fchmod to tighten a pre-existing file.é€  N)r­   Úopenr^   ÚO_WRONLYÚO_CREATÚO_TRUNCÚ_O_NOFOLLOWr   rt   ÚAttributeErrorÚfchmodÚwriteÚclose)rË   rÌ   Úfds   && rD   Ú_write_private_filerÛ   î   s‰   € ô 
�Š”�T“œBŸK™K¬"¯*©*Õ4´r·z±zÕAÄKÕOÐQVÓ	W€BðÜ”gœ~×.Õ.Ü�IŠI�b˜%Ô ÷ /ä
�Š�Ôä
�Š�Ž÷	 /×.ûô 	�Š��ús$   Á&C# ÂCÂC# ÃC 	ÃC# Ã#C;c               ó    € V ^8„  d   QhRRRR/# )r@   rÚ   r9   rA   r/   rB   )rC   s   "rD   rE   rE   ú   s   € ÷ ñ ˜ð  ñ rG   c                óÒ   €  \         P                  ! V 4      p\        \         R4      ;'       d#    VP                  \         P                  ! 4       8g  '       * #   \
         d     R# i ; i)zSFalse iff the open file is owned by another uid (same threat model as the pidfile).ÚgetuidT)r­   ÚfstatÚhasattrÚst_uidrÞ   rÖ   )rÚ   Ústs   & rD   Ú_fd_owned_by_usrã   ú   sM   € ðÜ�XŠX�b‹\ˆÜœB Ó)×FÐF¨b¯i©i¼2¿9º9»;Ñ.FÔGÐGøÜô Úðús   ‚,A ¯'A ÁA&Á%A&c               ó    € V ^8„  d   QhRRRR/# )r@   r‰   r/   rA   ztuple[Path, Path]rB   )rC   s   "rD   rE   rE     s   € ÷ ñ ˜Tð Ð.?ñ rG   c                ó¨  € \        4       pVR,          VR,          r2VP                  4       '       d"   VP                  4       '       d   V '       g   W#3# \        P                  ! R4      f   \	        R4      h\
        P                  ! RR7      ;_uu_ 4       p\        V4      R,          \        V4      R,          re\        RRR	\        V4      R
.^<RR7       \        RRRRRR\        V4      RRRRRRRRRR	\        V4      .^<RR7       VP                  VP                  R,           4      pVP                  RR7       \        WuP                  4       4       \        P                   ! Ws4       VP#                  VP                  4       4       \        P$                  ! VR4       RRR4       \&        P)                  RV4       W#3#   + '       g   i     L); i)zMGenerate (or return existing) 10-year CA cert + key via the host ``openssl``.úca.crtzca.keyÚopensslNzpopenssl not found on PATH. Install OpenSSL (apt: `openssl`, brew: `openssl`) to generate the iron-proxy CA cert.zhermes-proxy-ca-r•   Úgenrsaz-outÚ4096T)r˜   ÚcheckÚreqz-x509z-newz-nodesz-keyz-sha256z-daysÚ3650z-subjz/CN=hermes iron-proxy CAz-addextz!basicConstraints=critical,CA:TRUEzkeyUsage=critical,keyCertSignz.staged©Ú
missing_oki¤  zGenerated iron-proxy CA at %s)rw   rJ   r|   r}   r¢   rœ   r�   r   rž   r^   Úwith_suffixÚsuffixÚunlinkrÛ   Ú
read_bytesr­   r   Úwrite_bytesru   rƒ   r£   )r‰   ÚstateÚca_crtÚca_keyÚtmpdirÚtmp_keyÚtmp_crtÚ
key_stageds   $       rD   Úensure_ca_certrû     s—  € äÓ€EØ˜XÕ% u¨xÕ'7ˆFØ‡}�}‡‚˜6Ÿ=™=Ÿ?š?·5Øˆ~ÐÜ‡|‚|�IÓÒ&Üð  Nó  Oð  	OÜ	×	$Ò	$Ð,>×	?Õ	?À6Ü ›<¨(Õ2´D¸³LÀ8Õ4K�Üˆi˜ 6¬3¨w«<¸Ð@È"ÐTXÕYÜˆi˜ ¨°¸6Ä3ÀwÃ<ÐQZÐ\cÐekÐmtð  wQØÐ<¸iÐIhÐjpÔruÐv}Ór~ð@ð JLð  TXõ	Yð ×'Ñ'¨¯©¸	Õ(AÓBˆ
Ø×Ñ TÐÔ*Ü˜J×(:Ñ(:Ó(<Ô=Ü
�
Š
�:Ô&à×Ñ˜7×-Ñ-Ó/Ô0Ü
�Š�˜Ô÷ 
@ô ‡K�KÐ/°Ô8Øˆ>Ð÷ 
@×	?ús   ÂDGÇG	c               ó    € V ^8„  d   QhRRRR/# )r@   r–   r^   rA   rB   )rC   s   "rD   rE   rE     s   € ÷ Iñ I˜Sð I°cñ IrG   c                óŠ   € V  R\         P                  ! \        P                  ! ^ 4      4      P	                  4       R,           2# )z[Opaque token: recognizable prefix + 128-bit random hex suffix (iron-proxy matches exactly).Ú-:Né    N)ÚhashlibÚsha256r­   ÚurandomÚ	hexdigestr•   s   &rD   Úmint_proxy_tokenr    s3   € àˆX�Q”w—~’~¤b§j¢j°£nÓ5×?Ñ?ÓAÀ#ÕFÐGÐHÐHrG   c               ó    € V ^8„  d   QhRRRR/# )r@   Úpr   rA   r+   rB   )rC   s   "rD   rE   rE   !  s   € ÷ ñ ˜$ð  =ñ rG   c                ó|   €  V P                  RR7      P                  4       ;'       g    R#   \         d     R# i ; i)z>Stripped file contents, or None when missing/unreadable/empty.ú	utf-8-sig©rµ   N)Ú	read_textrÅ   rt   )r  s   &rD   Ú_read_text_or_noner  !  s;   € ðØ�{‰{ Kˆ{Ó0×6Ñ6Ó8×@Ð@¸DÐ@øÜô Úðús   ‚&, ©, ¬;º;c               ó   € V ^8„  d   QhRR/# rg   rB   )rC   s   "rD   rE   rE   )  s   € ÷ 6ñ 6 ñ 6rG   c                 ó$   € \        4       R,          # )zGManagement-token location; resolving it never creates or changes state.zmanagement.token©rn   rB   rG   rD   Ú_management_token_pathr  )  ó   € äÓ Ð#5Õ5Ð5rG   c               ó    € V ^8„  d   QhRRRR/# )r@   r‰   r/   rA   r^   rB   )rC   s   "rD   rE   rE   .  s   € ÷ ñ  dð °sñ rG   c                ó´   € \        4        \        4       pV '       g   \        V4      ;p'       d   V# \        RR7      p\	        WP                  R4      4       V# )aK  Return the management-API bearer key, minting it on first call.

Stored at the path from :func:`_management_token_path` with 0600 perms.
The daemon receives it via the ``HERMES_IRON_PROXY_MGMT_KEY`` env var
(named in the generated config's ``management.api_key_env``);
``hermes egress reload`` reads the same file to authenticate.
zhermes-mgmtr•   r™   )rw   r  r  r  rÛ   Úencode)r‰   r  ÚexistingÚtokens   $   rD   Úensure_management_tokenr  .  sJ   € ô ÔÜÓ €AßÔ"4°QÓ"7Ð7�hÖ7ØˆÜ MÔ2€EÜ˜Ÿ<™<¨Ó0Ô1Ø€LrG   c                 ó4   €  ^ RI p V #   \         d     R# i ; i)zIShared YAML helpers or None (not a hard requirement for proxy discovery).N)Úhermes_yamlÚImportError)Úyamls    rD   Ú_yamlr  @  s!   € ðÛ"ØˆøÜô Úðús   ‚ ˆ–c               ó   € V ^8„  d   QhRR/# )r@   rA   úOptional[tuple[str, int]]rB   )rC   s   "rD   rE   rE   J  s   € ÷ 
'ñ 
'Ð6ñ 
'rG   c                óÀ   € \        V \        4      '       d   RV 9  d   R# V P                  R4      w  rp \        V4      pT;'       g    RT3#   \         d     R# i ; i)z?``"host:port"`` -> ``(host, port)``; empty host means loopback.Ú:Nú	127.0.0.1)Ú
isinstancer^   Ú
rpartitionr9   Ú
ValueError)ÚlistenÚhostÚ_Úport_sÚports   &    rD   Ú_parse_listenr)  J  sf   € ô �fœc×"Ò" c°Ô&7ÙØ×'Ñ'¨Ó,�O€DˆVðÜ�6‹{ˆð ×Ð�K Ð&Ð&øô ô Úðús   µA ÁAÁAr4   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r@   Úsectionr^   Úkeysr4   r1   rA   r  rB   )rC   s   "rD   rE   rE   W  s/   € ÷ Mñ M˜Cð M¨ð M¸.ð MÐTmñ MrG   c               óš  a€ \        4       / rCVep   \        \        VP                  4      ;_uu_ 4        TP	                  T;'       g    \        4       R,          P                  RR7      4      ;'       g    / pRRR4       VP                  V 4      ;'       g    / o\        \        V3R lV 4       R4      4      #   + '       g   i     LJ; i)zu``(host, port)`` from the first truthy ``proxy.yaml[section][key]``, or None (also when file/ruamel.yaml is missing).Nú
proxy.yamlr  r	  c              3  óh   <"  € T F'  pSP                  V4      '       g   K  SV,          x € K)  	  R # 5irI   ©Úget)Ú.0ÚkÚblocks   & €rD   Ú	<genexpr>Ú!_config_listen.<locals>.<genexpr>^  s#   øé € ÐF±¨A¸¿¹À1¿œx˜u QŸxšx³ùs   ƒ2¡2rÁ   )
r  r   rt   Ú	YAMLErrorÚ	safe_loadrn   r
  r1  r)  Únext)r+  r4   r,  r  rÌ   r4  s   &$*  @rD   Ú_config_listenr:  W  s    ø€ ä“˜"ˆ$ØÒÜ”g˜tŸ~™~×.Õ.Ø—>‘> ;×#XÐ#XÔ3FÓ3HÈ<Õ3W×"cÑ"cÐmxÐ"cÓ"yÓz÷  Að  Að  AˆD÷ /à�H‰H�WÓ×#Ð# €EÜœÔF±ÓFÈÓKÓLÐL÷ /×.ús   ³AB:Á5B:Â:C
	c               ó    € V ^8„  d   QhRRRR/# )r@   r4   r1   rA   r  rB   )rC   s   "rD   rE   rE   a  s   € ÷ Kñ K°^ð KÐOhñ KrG   c                ó   € \        R RV R7      # )Ú
managementr$  ©r4   )r:  r>  s   &rD   Ú#_read_management_listen_from_configr?  a  s   € Ü˜,¨¸kÔJÐJrG   c               ó   € V ^8„  d   QhRR/# )r@   rA   ztuple[str, int]rB   )rC   s   "rD   rE   rE   e  s   € ÷ jñ j�ñ jrG   c                 ó<   € \        RRR4      ;'       g	    R\        3# )uç   Configured bind host/port to probe â€” on Linux the docker bridge, where a loopback connect would report a healthy
daemon as down.  ``tunnel_listen`` (CONNECT/MITM) falls back to ``http_listen`` for pre-listener-role-split configs.rl   Útunnel_listenÚhttp_listenr   )r:  rX   rB   rG   rD   Ú_probe_targetrD  e  s!   € ô ˜' ?°MÓB×iÐiÀ{ÔThÐFiÐirG   i¦  z`iron-proxy rejected the new config (validation failed; the running ruleset is unchanged): {body}i‘  u‰   management API rejected our key (401).  The running daemon was started with a different management.token â€” run `hermes egress restart`.c               ó   € V ^8„  d   QhRR/# r?   rB   )rC   s   "rD   rE   rE   r  s   € ÷ ñ �dñ rG   c            
     óD  € \        4       ;p '       d   \        V 4      '       g   \        R4      h\        4       ;pf   \        R4      h\	        \        4       4      ;p'       g   \        R4      hVw  r4\        P                  P                  RV RV R2RR	R
V 2/RR7      p \        P                  P                  V\        R7      ;_uu_ 4       pVP                  ^È8X  d    RRR4       R# \        RVP                   24      h  + '       g   i     R# ; i  \        P                  P                   d«   pRp\        \        4      ;_uu_ 4        TP!                  4       P#                  RRR7      R,          pRRR4       M  + '       g   i     M; i\$        P'                  TP(                  R4      p	\        T	P+                  TP(                  TR7      4      ThRp?i\        P                  P,                  \        3 d   p\        RT RT RT R24      ThRp?ii ; i)zu``POST /v1/reload`` (validation failures leave the running config untouched); actionable RuntimeError on any failure.uL   iron-proxy is not running â€” nothing to reload.  Run `hermes egress start`.Nz¡The generated proxy.yaml has no management listener (written before reload support).  Re-run `hermes egress setup` and use `hermes egress restart` this one time.u[   management.token is missing â€” re-run `hermes egress setup`, then `hermes egress restart`.zhttp://r  z
/v1/reloadÚPOSTrd   zBearer rG   )ÚmethodÚheadersrÌ   r—   Tz*management API returned unexpected status rÁ   r™   r   ©r¶   :Niô  Nz.management reload failed (HTTP {code}): {body})ÚcodeÚbodyz&could not reach the management API at z (zV).  If the daemon was started before reload support, run `hermes egress restart` once.)Ú	_read_pidÚ
_pid_aliver¢   r?  r  r  ÚurllibÚrequestÚRequestÚurlopenÚ_MGMT_RELOAD_TIMEOUTÚstatusÚerrorÚ	HTTPErrorr   rt   Úreadr¡   Ú_RELOAD_HTTP_ERRORSr1  rK  rC   ÚURLError)
r7   Úmgmtr  r%  r(  rë   Úrespr‡   rL  Úmessages
             rD   Úreload_proxyr]  r  sé  € ä“;ÐˆCÖ¤z°#§¢ÜÐiÓjÐjÜ3Ó5Ð5ˆÒ>Üð pó
ð 	
ô (Ô(>Ó(@ÓAÐAˆEÖAÜÐxÓyÐyØ�J€DÜ
�.‰.×
 Ñ
  7¨4¨&°°$°°zÐ!BÈ6Ð\kÐovÐw|Ðv}Ðm~Ð[ð  GJÐ
 ó  K€CðÜ�^‰^×#Ñ# CÔ1EÐ#×FÔFÈ$Ø�{‰{˜cÔ!Ø÷ GÑFô Ð!KÈDÏKÉKÈ=ÐYÓZÐZ÷ G×FÐFûô �<‰<×!Ñ!ô NØˆÜ”g×ÕØ—8‘8“:×$Ñ$ W°YÐ$Ó?ÀÕEˆD÷ ××Ðúä%×)Ñ)¨#¯(©(Ð4dÓeˆÜ˜7Ÿ>™>¨s¯x©x¸d˜>ÓCÓDÈ#ÐMûÜ�L‰L×!Ñ!¤7Ð+ô äØ4°T°F¸!¸D¸6ÀÀCÀ5ð  I_ð  `ó
àð	ûðúsg   Â"-D ÃDÃ"D Ã,DÄD	ÄD ÄD ÄHÄ7GÅ)FÅ9	GÆFÆAGÇ&HÈHÈHÈHc               ó    € V ^8„  d   QhRRRR/# )r@   r:   r9   rA   r<   rB   )rC   s   "rD   rE   rE   �  s   € ÷ 
(ñ 
( cð 
(¨iñ 
(rG   c                ó®   € \         P                  ! 4       R8X  d7   \        4       ;p'       d   VR8w  d	   V RV  2.# \        P	                  R4       RV  2.# )a  Single bind (v0.39 allows one): docker bridge on Linux (what ``host.docker.internal`` resolves to; loopback is
unreachable from containers), loopback on Docker Desktop (VPNkit).  NEVER 0.0.0.0: a LAN peer with a leaked
sandbox token could spend the operator's API quota.ÚLinuxr   r  u­   No docker bridge (docker0) detected â€” binding iron-proxy to loopback only.  Docker sandboxes will NOT be able to reach the proxy until it is restarted with docker running.ú
127.0.0.1:)Úplatformr…   Ú_detect_docker_bridge_iprƒ   r„   )r:   Ú	bridge_ips   & rD   Ú_default_http_listenre  �  s`   € ô ‡‚Ó˜GÔ#Ü1Ó3Ð3ˆIÖ3¸ÀkÔ9QØ �k  ; -Ð0Ð1Ð1Ü�‰ð |ô	
ð ˜˜Ð&Ð'Ð'rG   c               ó   € V ^8„  d   QhRR/# ©r@   rA   r+   rB   )rC   s   "rD   rE   rE   �  s   € ÷ ñ  -ñ rG   c                 ó†  €  \        . RO^RR7      p T P                  ^ 8X  d"   R T P
                  P                  4        4       MRp\        R T 4       R4      p \        P                  ! T4      pTP                  '       g[   TP                  '       gI   TP                  '       g7   TP                  '       g%   TP                   '       g   TP"                  '       d   \$        P'                  RT4       R# \)        T4      #   \        \        P                  3 d     R# i ; i  \        P                  \        3 d     R# i ; i)	z¸docker0 IPv4 via ``ip -4 addr show docker0``, or None.  SECURITY: validated through
:mod:`ipaddress` so a hostile ``ip`` shim can't inject 0.0.0.0/loopback/multicast/link-local/public.T©r˜   r±   Nc              3  ó@   "  € T F  qP                  4       x € K  	  R # 5irI   )Úsplit)r2  Úlines   & rD   r5  Ú+_detect_docker_bridge_ip.<locals>.<genexpr>¥  s   é € Ð>Ñ&=˜d�Z‰Z�\ˆ\Ó&=ùs   ‚c              3  ó¢   "  € T FE  pR VRR 9   g   K  WP                  R 4      ^,           ,          P                  R4      ^ ,          x € KG  	  R# 5i)ÚinetNÚ/éÿÿÿÿ)Úindexrk  )r2  Úpartss   & rD   r5  rm  ¦  sK   é € ÐnÑPUÀuÐY_ÐchÐilÐjlÐcmÑYmÔB�eŸK™K¨Ó/°!Õ3Õ4×:Ñ:¸3Ó?À×BÒBÓPUùs
   ‚A“<AzORefusing suspicious docker bridge IP %s reported by `ip`; skipping bridge bind.)Úipz-4ú-oÚaddrÚshowÚdocker0rB   )rž   rt   rº   rÃ   rŸ   rÄ   Ú
splitlinesr9  Ú	ipaddressÚIPv4AddressÚAddressValueErrorr#  Úis_unspecifiedÚis_loopbackÚis_multicastÚis_reservedÚis_link_localÚ	is_globalrƒ   r„   r^   )rÇ   ÚlinesÚ	candidaterv  s       rD   rc  rc  �  s  € ðÜÒ@È!ÐRVÔWˆð CFÇ.Á.ÐTUÔBUÑ> c§j¡j×&;Ñ&;Ô&=Ô>Ð[]€EÜÑnÑPUÓnÐptÓu€IðÜ×$Ò$ YÓ/ˆð ××Ð˜d×.×.Ð.°$×2C×2CÐ2CÀt×GW×GWÐGWÐ[_×[m×[mÐ[mÐqu×q×qÐqÜ�‰ÐhÐjsÔtÙÜˆt‹9Ðøô ”Z×.Ñ.Ð/ô Úðûô ×'Ñ'¬Ð4ô Úðús#   ‚C? ÁD! Ã?DÄDÄ!E Ä?E r:   Ú	audit_logÚallowed_hostsÚupstream_deny_cidrsrC  c               ó<   € V ^8„  d   QhRRRRRRRRRR	R
RRRRRRR/	# )r@   Úmappingsúlist[TokenMapping]Úca_certr   rö   r:   r9   r…  r1   r†  úOptional[list[str]]r‡  rC  rA   ÚdictrB   )rC   s   "rD   rE   rE   ±  s^   € ÷ -ñ -Ø#ð-Ø.2ð-Ø<@ð-ØORð-ð wEð-à&ð-àDWð-ð nAð-ð 
ñ-rG   c                ó:  € \        T;'       g    \        4      pR V  4        F  p	W˜9  g   K  VP                  V	4       K  	  \        Vf   \        MT4      p
V  UU	u. uFf  pRRRRVP                  /RRVP
                  R\        VP                  ;'       g    R24      R	R
RRRR
/RVP                   U	u. uF  p	RV	/NK	  	  up	/NKh  	  ppp	V'       d   \        V4      M\        V4      ;'       g    RV 2.^ ,          pVP                  R^4      ^ ,          ;'       g    RpRRRRR/RRVRV RV^,            2RRRR3R^ RRRV
/R RR/R!RRV\        ,            2R"\        /R#R$\        V4      R%\        V4      R&R'R(^¨/R)R*R+R,R-V//R*R.R,R.V//.R/R0R1//# u up	i u up	pi )4a   iron-proxy YAML config dict (v0.39.0 schema).  Real secrets come from iron-proxy's OWN env (``source: {type: env}``);
the sandbox never sees them.  ``upstream_deny_cidrs=None`` = default SSRF deny list, ``[]`` opts out.
``audit_log`` is forward-compat only (v0.39 rejects ``audit_path``).c              3  óH   "  € T F  qP                    F  q"x € K  	  K  	  R # 5irI   )ra   )r2  ÚmÚhs   &  rD   r5  Ú%build_proxy_config.<locals>.<genexpr>¹  s   é € Ð<™�A×+;Ô+; aŒaÑ+;‰a›ùs   ‚ "ÚsourceÚtyperÀ   Úvarr   Úproxy_valuer   Úmatch_queryTÚ
match_bodyFÚrequireÚrulesr%  ra  r  r   Údnsr$  z127.0.0.1:0Úproxy_iprl   rB  rC  Úhttps_listenÚmax_request_body_bytesÚmax_response_body_bytesÚ upstream_response_header_timeoutÚ120sr‡  Úmetricsr=  Úapi_key_envÚtlsr‹  rö   Úcert_cache_sizeiè  Úleaf_cert_expiry_hoursÚ
transformsÚnameÚ	allowlistÚconfigÚdomainsÚsecretsÚlogÚlevelr£   rc   i   )rY   r   Úappendr#   r`   r_   r   ra   re  ÚrsplitÚ_MGMT_PORT_OFFSETÚ_MGMT_API_KEY_ENVr^   )r‰  r‹  rö   r:   r…  r†  r‡  rC  r   r‘  Ú
deny_cidrsr�  Úsecrets_rulesÚprimary_listenÚ	bind_hosts   $$$$$$$$       rD   Úbuild_proxy_configr·  ±  s$  € ô ˜M×CÐCÔ-CÓD€EÙ<™Ö<ˆØŽ>Ø�L‰L˜ŽOñ =ô Ð6IÒ6QÕ2ÐWjÓk€Jñ ôñ ˆ!ð 	�6˜5 %¨¯©Ð9ØØ˜1Ÿ=™=¨/¼4ÀÇÁ×@eÐ@eÐSeÓ;fØ˜4 ¨u°iÀð
ð 	 q×'7Ò'7Ó8Ñ'7 !�6˜1“+Ñ'7Ñ8óñ ð ñ ÷ ,7”d˜;Ô'Ô<PÐQ\Ó<]×<}Ð<}ÐdnÐozÐn{Ðb|Ða}ð  @Aõ  B€NØ×%Ñ% c¨1Ó-¨aÕ0×?Ð?°K€Ið 	�˜-¨°[ÐAØà˜^¨]¸y¸kÈÈ;ÐYZÍ?ÐJ[Ð<\Ø˜MØ$Ð&6Ð8QÐSTØ.°Ð8MÈzð
ð 	�H˜mÐ,à�x :¨kÔ<MÕ.MÐ-NÐ!OÐQ^Ô`qÐrØ�	œ3˜w›<¨´3°v³;Ð@QÐSWÐYqÐsvÐwØ˜ ¨X¸	À5Ð7IÐJÈVÐU^Ð`hÐktð  wDð  kEð  MFð  GØ�˜Ð ð!ð ùò 9ùós   Á:FÂFÂ4FÃFÆFc               ó$   € V ^8„  d   QhRRRRRR/# )r@   rË   r   Ústrict_chmodr/   rA   r9   rB   )rC   s   "rD   rE   rE   á  s!   € ÷ 
ñ 
˜tð 
°dð 
¸sñ 
rG   c               ó`  € \         P                  ! \        V 4      \         P                  \         P                  ,          \         P
                  ,          \        ,          R4      p \         P                  ! VR4       V#   \         d$    T'       d   \         P                  ! T4       h  T# i ; i)z®O_APPEND|O_CREAT 0o600 + O_NOFOLLOW (planted symlinks refused); fchmod tightens a pre-existing
file (failure fatal iff ``strict_chmod``).  Raises OSError; caller owns the fd.rÐ   )
r­   rÑ   r^   rÒ   rÓ   ÚO_APPENDrÕ   r×   rt   rÙ   )rË   r¹  rÚ   s   &$ rD   Ú_open_private_appendr¼  á  s~   € ô 
�Š”�T“œBŸK™K¬"¯*©*Õ4´r·{±{ÕBÄ[ÕPÐRWÓ	X€BðÜ
�	Š	�"�eÔð
 €Iøô	 ô ßÜ�HŠH�RŒLØð ð €Ið	ús   Á&A? Á?B-ÂB-Â,B-c               ó    € V ^8„  d   QhRRRR/# )r@   Ú
audit_pathr   rA   rÎ   rB   )rC   s   "rD   rE   rE   î  s   € ÷ ñ  ð ¨$ñ rG   c                ó–   €  \         P                  ! \        V RR7      4       R#   \         d   p\	        RT  RT R24      ThRp?ii ; i)zdPre-create the audit log 0o600 (forward-compat: v0.39 never writes it); RuntimeError on any OSError.T©r¹  z2Refusing to start: could not pre-create audit log z with restrictive permissions (z;).  Move or chmod any existing file at that path and retry.N)r­   rÙ   r¼  rt   r¢   )r¾  r‡   s   & rD   Úensure_audit_logrÁ  î  sX   € ðÜ
�ŠÔ% j¸tÔDÖEøÜô ÜØ@ÀÀÐLkÐloÐkpð  qlð  mó
àð	ûðús   ‚!% ¥A°AÁAc               ó    € V ^8„  d   QhRRRR/# )r@   rª  r�  rA   r   rB   )rC   s   "rD   rE   rE   ø  s   € ÷ ñ ˜tð ¨ñ rG   c           	     ó˜   € \        4       ;pf   \        R4      h\        4       R,          p\        W!P	                  V RRR7      RR7       V# )z®Serialize the config dict to ``<hermes_home>/proxy/proxy.yaml`` (safe_dump, no Python tags).

The file holds proxy tokens: written 0600 from creation, never at process umask.zLruamel.yaml is required to write the iron-proxy config but is not installed.r.  F)Údefault_flow_styleÚ	sort_keysrÐ   ©Úmode)r  r¢   rw   r   Ú	safe_dump)rª  r  rË   s   &  rD   Úwrite_proxy_configrÉ  ø  sI   € ô “ˆˆÒ ÜÐiÓjÐjÜÓ Õ,€DÜ�dŸN™N¨6ÀeÐW\˜NÓ]ÐdiÕjØ€KrG   c               ó    € V ^8„  d   QhRRRR/# )r@   r‰  rŠ  rA   r   rB   )rC   s   "rD   rE   rE     s   € ÷ ñ Ð/ð °Dñ rG   c                ó&  € R^RV  Uu. uF\  pRVP                   RVP                  R\        VP                  4      R\        VP                  4      R\        VP
                  4      /NK^  	  up/p\        4       R,          p\        W2R	R
7       V# u upi )zaPersist sandbox-visible tokens to ``mappings.json`` (read by the Docker backend, not iron-proxy).ÚversionÚtokensr_   Úenv_namera   r   rb   úmappings.jsonrÐ   rÆ  )r_   r`   rY   ra   r   rb   rw   r   )r‰  r�  ÚpayloadrË   s   &   rD   Úwrite_mappingsrÑ    s—   € à˜!˜Xñ ó(ñ ˆ!ð 	�q—}‘} j°!·/±/ÐCSÔUYÐZ[×ZjÑZjÓUkØœ˜aŸo™oÓ.Ð0AÄ4È×HYÑHYÓCZó)ñ ñ(ð €Gô Ó Õ/€DÜ�d¨%Õ0Ø€Kùò(s   ˆA"B
c               ó   € V ^8„  d   QhRR/# )r@   rA   rŠ  rB   )rC   s   "rD   rE   rE     s   € ÷ ñ Ð)ñ rG   c                 óô  € \        4       R,          ;p P                  4       '       g   . #  \        P                  ! V P	                  RR7      4      p. pTP                  R. 4       Fº  p\        \        \        4      ;_uu_ 4        TP                  \        TR,          TR,          \        TP                  R	4      ;'       g    R4      \        TP                  R
4      ;'       g    R4      \        TP                  R4      ;'       g    R4      4      4       RRR4       K¼  	  T#   \
        \        P                  3 d#   p\        P                  RT4       . u Rp?# Rp?ii ; i  + '       g   i     EK  ; i)z;Read mappings.json, if it exists.  Empty list on any error.rÏ  r  r	  z+Failed to read iron-proxy mappings.json: %sNrÍ  r_   rÎ  ra   r   rb   rB   rc   )rw   rJ   ÚjsonÚloadsr
  rt   ÚJSONDecodeErrorrƒ   r„   r1  r   ÚKeyErrorÚ	TypeErrorr¯  r]   Útuple)ÚfrÐ  r‡   rÈ   Úitems        rD   Úload_mappingsrÜ    s>  € ä!Ó# oÕ5Ð5ˆA×=Ñ=×?Ò?Øˆ	ðÜ—*’*˜QŸ[™[°+˜[Ó>Ó?ˆð !€CØ—‘˜H bÖ)ˆÜ”h¤	×*Õ*Ø�J‰J”| D¨Õ$7¸¸jÕ9IÌ5ÐQU×QYÑQYÐZjÓQk×QqÐQqÐoqÓKrÜ$)¨$¯(©(°?Ó*C×*YÐ*YÐGYÓ$ZÔ\aÐbf×bjÑbjÐk|Ób}÷  cDð  cDð  BDó  ]EóFô G÷ +Ñ*ñ *ð €Jøô ”T×)Ñ)Ð*ô Ü�‰ÐDÀcÔJØ�	ûðú÷
 +×*Ñ*ús6   «&D% ÂA E%Ã%"E%ÄE%Ä%E"Å EÅE"ÅE"Å%E7Úavailable_env_namesc               ó    € V ^8„  d   QhRRRR/# )r@   rÝ  rŒ  rA   rŠ  rB   )rC   s   "rD   rE   rE     s   € ÷ ñ Ð7Jð ÐVhñ rG   c                ó8  a€ V e   \        V 4      M;\        P                  P                  4        UUu0 uF  w  rV'       g   K  VkK  	  uppo\        P                  4        UUu. uF
  w  r4W4R
R3NK  	  upp\
        P                  4        UUu. uFM  w  r5T\        VR,          4      \        VR,          4      \        VP                  R4      ;'       g    R4      3NKO  	  upp,           pV UUU	U
u. uF„  w  rxršVS9   gC   \        ;QJ d    V3R lV
 4       F  '       g   K   RM	  RM! V3R lV
 4       4      '       g   KO  \        \        VP                  4       P                  RR4      R	7      WxWš4      NK†  	  up
p	pp# u uppi u uppi u uppi u up
p	ppi )a   One TokenMapping per known provider whose env var is set (bearer providers first).  Canonical OR any alias
present -> ONE mapping on the canonical name (the subprocess-env builder mirrors aliases).
``available_env_names`` (Bitwarden adapter) overrides the non-empty names in the host env.r   r   r   c              3  ó,   <"  € T F	  qS9   x € K  	  R # 5irI   rB   )r2  ÚaÚnamess   & €rD   r5  Ú-discover_provider_mappings.<locals>.<genexpr>*  s   øé € Ð#@¹°1¨¦J»ùs   ƒTFÚ_api_keyrÁ   r•   rc   rB   )Úsetr­   r®   Úitemsr   r!   rÙ  r1  Úanyr]   r  Úlowerr   )rÝ  r3  Úvr¯   r‘  ÚsÚspecsrÎ  r   rI  r   râ  s   $          @rD   Údiscover_provider_mappingsrì    sg  ø€ ð )<Ò(GŒCÐ#Ô$Ô\^×\fÑ\f×\lÑ\lÔ\nÔMtÑ\nÑTXÐTUÔrsÏaÈaÑ\nÒMt€EÜ8I×8OÑ8OÔ8QÔRÑ8Q±°ˆaÐ&¨Ó+Ñ8QÒRÜe{÷  fBñ  fBô  fDôVñ fDÑ]aÐ]^ˆŒE�!�G•*Óœu Q Õ%7Ó8¼%ÀÇÁÀiÓ@P×@VÐ@VÐTVÓ:WÓXñ  fDòVõ €Eñ
 27öá16Ñ-ˆH˜WØ�uÔ§£Ô#@¹Ó#@§§¢Ô#@¹Ó#@× @ô 	{ŒÔ%¨X¯^©^Ó-=×-EÑ-EÀjÐRTÓ-UÔVÐX`ÐipÖzÙ16ôð ùó	 NuùÛRùó Vùõs0   ²FÁFÁ$FÂAFÃ2F
ÄF
Ä#F
Å9F
c               ó    € V ^8„  d   QhRRRR/# )r@   rÝ  rŒ  rA   r<   rB   )rC   s   "rD   rE   rE   .  s   € ÷ <ñ <Ð9Lð <ÐXañ <rG   c                óê   € V e   \        V 4      M;\        P                  P                  4        UUu0 uF  w  rV'       g   K  VkK  	  uppp\         Uu. uF  qDV9   g   K  VNK  	  up# u uppi u upi )zREnv names of recognized providers the proxy can't swap (SigV4 / SDK-minted OAuth).)rå  r­   r®   ræ  r"   )rÝ  r3  ré  râ  r¯   s   $    rD   Údiscover_uncovered_providersrï  .  se   € à(;Ò(GŒCÐ#Ô$Ô\^×\fÑ\f×\lÑ\lÔ\nÔMtÑ\nÑTXÐTUÔrsÏaÈaÑ\nÒMt€EÝ,Ó;Ñ,�!°U±
�AˆAÑ,Ñ;Ð;ùó NuùÚ;s   ±A*ÁA*ÁA0Á!A0Úrotatec               ó(   € V ^8„  d   QhRRRRRRRR/# )r@   r  rŠ  Ú
discoveredrð  r/   rA   rB   )rC   s   "rD   rE   rE   4  s2   € ÷ Cñ CÐ 2ð CÐ@Rð CÐ\`ð CÐmñ CrG   c                óô   € V'       d   / MV  Uu/ uF  q3P                   VbK  	  uppV Uu. uF<  qUP                   V9   d(   \        WTVP                   ,          P                  R7      MTNK>  	  up# u upi u upi )z²Existing tokens are preserved (containers baked with them keep working), hosts/headers/aliases refresh
from ``discovered``; ``rotate=True`` re-mints; undiscovered providers drop.)r_   )r`   r   r_   )r  rò  rð  r�  Úby_namerv   s   $$$   rD   Úmerge_mappingsrõ  4  s€   € ÷ ‰b¹XÓ F¹X¸§¡°!Ò!3¹XÑ F€Gñ xBó  Cñ  xBÐrsÏOÉOÐ_fÔLfŒG�A¨1¯?©?Õ#;×#GÑ#GÕHÐlmÒmñ  xBñ  Cð  Cùò !Gùò Cs   �A0«AA5c               ó   € V ^8„  d   QhRR/# rg   rB   )rC   s   "rD   rE   rE   ;  s   € ÷ 1ñ 1�$ñ 1rG   c                 ó$   € \        4       R ,          # )úiron-proxy.pid)rw   rB   rG   rD   Ú_pidfilerù  ;  s   € ÜÓÐ 0Õ0Ð0rG   c               ó   € V ^8„  d   QhRR/# )r@   rA   r6   rB   )rC   s   "rD   rE   rE   ?  s   € ÷ $ñ $�=ñ $rG   c                 ó¼   €  \        \        4       R ,          P                  RRR7      P                  4       4      p T ^ 8”  d   T # R#   \        \
        3 d     R# i ; i)rø  r  r   )rµ   r¶   N)r9   rn   r
  rÅ   rt   r#  )r7   s    rD   rM  rM  ?  sb   € ðÜÔ&Ó(Ð+;Õ;×FÑFÐP[ÐdmÐFÓn×tÑtÓvÓwˆð ˜”'ˆ3Ð#˜tÐ#øô ”ZÐ ô âðús   ‚9A ÁAÁAc               ó    € V ^8„  d   QhRRRR/# )r@   r7   r9   rA   r+   rB   )rC   s   "rD   rE   rE   H  s   € ÷ 4ñ 4˜Sð 4 ]ñ 4rG   c                ó  €  \        RV  R24      P                  RR7      pTP                  R4      ;p^ 8¼  d   Y^,           R P	                  4       M. p\        T4      ^8”  d
   T^,          # R#   \         d     R# i ; i)u[   /proc/<pid>/stat starttime (field 22) on Linux, else None â€” cheap PID-recycling detector.ú/proc/z/statr™   r	  NÚ))r   r
  rt   Úrfindrk  Úlen)r7   r±   ÚrparenÚfieldss   &   rD   Ú_pid_proc_starttimer  H  s†   € ðÜ�f˜S˜E Ð'Ó(×2Ñ2¸GÐ2ÓDˆð 6:·Z±ZÀ³_Ð+D¨6ÈÔ*JˆT˜1•*�+Ð×$Ñ$Ô&ÐPR€FÜ˜V› rÔ)ˆ6�"�:Ð3¨tÐ3øô	 ô Úðús   ‚A/ Á/A>Á=A>c               ó   € V ^8„  d   QhRR/# rg   rB   )rC   s   "rD   rE   rE   S  s   € ÷ 6ñ 6˜tñ 6rG   c                 ó$   € \        4       R,          # )zkOn-disk nonce sibling of the pidfile, so stop/status in a later CLI process can still defeat PID recycling.ziron-proxy.noncer  rB   rG   rD   Ú_persisted_nonce_pathr  S  r  rG   c               ó   € V ^8„  d   QhRR/# rg  rB   )rC   s   "rD   rE   rE   X  s   € ÷ ñ ˜}ñ rG   c                 óä  €  \         P                  ! \        \        4       4      \         P                  \
        ,          4      p  \        T 4      '       g    \         P                  ! T 4       R# \         P                  ! T R4      P                  RRR7      P                  4       ;'       g    R \         P                  ! T 4       #   \         d     R# i ; i  \         P                  ! T 4       i ; i)z­Nonce from disk, or None if missing/unreadable/empty/not owned by us (callers fall back to argv0
matching).  O_NOFOLLOW: this read decides whether stop_proxy SIGKILLs a PID.Né   r™   ÚignorerJ  )r­   rÑ   r^   r  ÚO_RDONLYrÕ   rt   rã   rÙ   rW  r¡   rÅ   )rÚ   s    rD   Ú_read_persisted_noncer  X  sª   € ðÜ�WŠW”SÔ.Ó0Ó1´2·;±;ÄÕ3LÓMˆðÜ˜r×"Ò"Øô 	�Š�Žô �wŠw�r˜3Ó×&Ñ& w°xÐ&Ó@×FÑFÓH×PÐPÈDÐPä
�Š��øô ô Úðûô 	�Š��ús*   ‚AC ÁC Á.<C Â+C ÃCÃCÃC/c               ó    € V ^8„  d   QhRRRR/# )r@   r7   r9   rA   r/   rB   )rC   s   "rD   rE   rE   g  s   € ÷  ñ  �Cð  ˜Dñ  rG   c           	     ó  a€ V ^ 8:  d   R#  ^ RI pVP                  V 4      '       g   R#  \        P                  \        \        4       34       Uu. uF  q"'       g   K  TNK  	  ppT'       dŠ   \        \        4      ;_uu_ 4        \        RT  R24      P!                  4       o\"        ;QJ d    T3R lT 4       F  '       g   K   RM	  RM! T3R lT 4       4      '       d    RRR4       R#  RRR4       \        \        4      ;_uu_ 4        \        RT  R24      ;pP%                  4       '       dn   \
        P&                  P)                  TP!                  4       P+                  R	4      ^ ,          P-                  R
RR7      4      P/                  R4      uuRRR4       #  RRR4       \        \        \0        P2                  4      ;_uu_ 4        \5        RR\7        T 4      RR.^RR7      pTP8                  ^ 8X  d[   \
        P&                  P)                  TP:                  ;'       g    RP=                  4       4      P/                  R4      uuRRR4       #  RRR4       R#   \         dX    \        P                  ! 4       R8w  d:    \
        P                  ! T ^ 4        ELx  \        \        \        3 d      R# i ; i EL™i ; iu upi   + '       g   i     ELç; i  + '       g   i     ELH; i  + '       g   i     R# ; i)a  True iff ``pid`` is alive AND an iron-proxy process.  PID-reuse defense, in priority order: nonce in
/proc/<pid>/environ, argv[0] basename in /proc/<pid>/cmdline, ``ps -o comm=`` basename (a loose
``"iron-proxy" in cmdline`` would hit ``tail iron-proxy.log``).FNÚWindowsrþ  z/environc              3  ó\   <"  € T F!  p\          R V 2P                  4       S9   x € K#  	  R# 5i)Ú=N)Ú_HERMES_IRON_PROXY_NONCE_ENVr  )r2  r¯   Ú	env_bytess   & €rD   r5  Ú_pid_alive.<locals>.<genexpr>}  s/   øé € ÐiÑXhÐSTÔ2Ð3°1°Q°CÐ8×?Ñ?ÓAÀYÖNÓXhùs   ƒ),Tz/cmdlineó    r™   r  rJ  r{   Úpsz-pru  zcomm=ri  rÁ   )ÚpsutilÚ
pid_existsr  rb  r…   r­   ÚkillÚProcessLookupErrorÚPermissionErrorrt   r�  Úfromkeysr,   r  r   r   rò   rç  rJ   rË   Úbasenamerk  r¡   Ú
startswithrº   rÃ   rž   r^   rŸ   rÄ   rÅ   )r7   r  r¯   Únonce_candidatesÚcmdline_pathrÇ   r  s   &     @rD   rN  rN  g  sM  ø€ ð ˆa„xÙð
ãØ× Ñ  ×%Ò%Ùð &ô $(§=¡=´,Ô@UÓ@WÐ1XÔ#YÓ_Ñ#Y˜aÓ]^Ÿ˜Ñ#YÐÐ_ßÜ”g×ÕÜ˜v c U¨(Ð3Ó4×?Ñ?ÓAˆIß‹sÔiÑXhÓi�s�sŠsÔiÑXhÓi×iÒiØ÷ Ñài÷ ô 
”'×	Õ	Ü  6¨#¨¨hÐ!7Ó8Ð8ˆL×@Ñ@×BÒBÜ—7‘7×#Ñ# L×$;Ñ$;Ó$=×$CÑ$CÀGÓ$LÈQÕ$O×$VÑ$VÐW^ÐgoÐ$VÓ$pÓq×|Ñ|ð  ~Jó  K÷ 
Ò	ØB÷ 
ô 
”'œ:×4Ñ4×	5Õ	5Ü�D˜$¤ C£¨$°Ð8À!È$ÔOˆØ�>‰>˜QÔÜ—7‘7×#Ñ# S§Z¡Z×%5Ð%5°2×$<Ñ$<Ó$>Ó?×JÑJÈ<ÓX÷ 
6Ò	5à÷ 
6ñ
 øô+ ô Ü�?Š?Ó 	Ô)ðÜ—’˜˜Q—øÜ&¬¼ÐAô Ûðúò *ðüò `ç×Ðú÷ 
×	Ð	ú÷ 
6Ö	5ñ
 úss   ŒI" Á	KÁKÂ=KÃKÃ"KÄBK ÇAK4È)#K4É"%KÊJ"Ê"J=Ê8KÊ<J=Ê=KËKËK	Ë K1	Ë4L	r€   Ú	extra_envTÚrefresh_secrets_from_bitwardenÚbitwarden_configc               ó4   € V ^8„  d   QhRRRRRRRRRRR	R
RR/# )r@   r€   r1   r4   r"  úOptional[dict[str, str]]rx   r/   r#  r$  úOptional[dict]rA   r.   rB   )rC   s   "rD   rE   rE   Š  sF   € ÷ 7ñ 7Øð7Ø3Að7ØUmð7àð7àEIð7àesð7ð ñ7rG   c           	     óR  aaaa€ \        4       ;p'       d   \        V4      '       d   \        4       # T ;'       g    \        VR7      ;pf   \	        R4      hT;'       g    \        4       R,          ;pP                  4       '       g   \	        RV R24      h\        W$VR7      p	\        V4      e   \        4       V	\        &   \        P                  ! \        P                  ! ^4      4      P                  4       s\         V	\"        &   \        4       R,          o\%        WxV	S4      o\'        4       o \)        SSP*                  4       R
 TTT3R lloTT3R lp
TT3R lp\/        4       w  rÍ\1        T
4      ;_uu_ 4        \3        SYÍTR7      pRRR4       SP5                  4       e   T! 4       hX'       g   S! RT RT R\6         R2RR7      h\8        P;                  RSP*                  T4       \        4       #   \         d    \-        S^R	7       h i ; i  + '       g   i     L‘; i)uÏ   Spawn iron-proxy as a managed background subprocess (idempotent if already running).  ``refresh_secrets_from_bitwarden``
re-fetches secrets from BWS â€” the ``credential_source: bitwarden`` rotation promise.©rx   Nu@   iron-proxy binary not available â€” run `hermes egress install`.r.  ziron-proxy config not found at z". Run `hermes egress setup` first.)r"  Úrefresh_from_bitwardenr$  ziron-proxy.log©Úgrace_secondsc               ó$   € V ^8„  d   QhRRRRRR/# )r@   Úmsgr^   r  r/   rA   r¢   rB   )rC   s   "rD   rE   Ú!start_proxy.<locals>.__annotate__©  s!   € ÷ =ñ =�Cð = $ð =¨<ñ =rG   c               óŒ   <€ \        S^R7      pV'       d   \        S^R7       SP                  RR7       \        V  RV 24      # )é   )rƒ  r+  Trí   zLast log lines:
)Ú	_tail_logÚ_kill_and_waitrñ   r¢   )r.  r  ÚtailÚlog_pathÚpidfileÚprocs   &$ €€€rD   Ú_abortÚstart_proxy.<locals>._abort©  sA   ø€ Ü˜¨Ô,ˆßÜ˜4¨qÕ1Ø�‰ $ˆÔ'Ü˜s˜eÐ#4°T°FÐ;Ó<Ð<rG   c                óV   <€ \        S^R7       SP                  RR7       \        4       h)r@   r+  Trí   )r3  rñ   ÚKeyboardInterrupt)Ú_signumÚ_framer6  r7  s   &&€€rD   Ú_interrupt_handlerÚ'start_proxy.<locals>._interrupt_handler°  s$   ø€ Ü�t¨1Õ-Ø�‰ $ˆÔ'ÜÓ!Ð!rG   c                 ó4   <€ S ! R SP                    R2RR7      # )z$iron-proxy exited immediately (code z). F©r  )rŸ   )r8  r7  s   €€rD   Ú<lambda>Ústart_proxy.<locals>.<lambda>µ  s   ø€ ™FÐ%IÈ$Ï/É/ÐIZÐZ]Ð#^ÐejÕkrG   )Úon_exitziron-proxy did not bind r  z within zs.  Process was killed.  TrA  z#Started iron-proxy pid=%s config=%s)rM  rN  Ú
get_statusrˆ   r¢   rw   rJ   Ú_build_proxy_subprocess_envr?  r  r²  r   r  r­   r  r  r,   r  Ú_spawn_daemonrù  Ú_write_pidfile_safelyr7   r3  rD  Ú_interrupt_guardÚ_await_listeningÚpollÚ_STARTUP_GRACE_SECONDSrƒ   r£   )r€   r4   r"  rx   r#  r$  r  Úbin_pathÚcfgrÀ   r>  Ú_exited_errorÚ
probe_hostr:   r8   r8  r5  r6  r7  s   $$$$$$         @@@@rD   Ústart_proxyrQ  Š  sæ  û€ ô “KÐˆÖ¤Z°×%9Ò%9Ü‹|ÐØ×TÐTœoÐASÔTÐTˆÒ]ÜÐ]Ó^Ð^Ø×EÐEÔ#3Ó#5¸Õ#DÐEˆC×MÑM×OÒOÜÐ<¸S¸EÐAcÐdÓeÐeä
%°	ð  DTô  U€Cä*¨3Ó/Ò;Ü!8Ó!:ˆÔÑä—>’>¤"§*¢*¨R£.Ó1×;Ñ;Ó=€LÜ(4€CÔ$Ñ%ÜÓ!Ð$4Õ4€HÜ˜¨¨XÓ6€Dä‹j€GðÜ˜g t§x¡xÔ0÷
=ò =ö"õ
 l€Mä+›oÑ€JÜ	Ð,×	-Õ	-Ü$ T¨:ÈMÔZˆ	÷ 
.ð ‡y�yƒ{ÒÙ‹oÐçÙÐ/°
¨|¸1¸[¸MÈÔRhÐQið  jCð  Dð  KOô  Pð  	PÜ
‡K�KÐ5°t·x±xÀÔEÜ‹<Ðøô9 ô Ü�t¨1Õ-Øðú÷& 
.×	-ús   Ä0G9 Å>HÇ9HÈH&	c              #  óÐ  "  € \         P                  ! 4       R8X  g-   \        P                  ! 4       \        P                  ! 4       Jd   Rx € R# \
        P                  \
        P                  3 Uu. uF  q\
        P
                  ! W4      3NK  	  pp Rx € V F  w  r\
        P
                  ! W4       K  	  R# u upi   T F  w  r\
        P
                  ! Y4       K  	  i ; i5i)zmRoute SIGINT/SIGTERM to ``handler`` for the block (POSIX main thread only); previous handlers restored after.r  N)rb  r…   Ú	threadingÚcurrent_threadÚmain_threadÚsignalÚSIGINTÚSIGTERM)ÚhandlerÚsigÚprevÚolds   &   rD   rI  rI  Ä  s¥   é € ô ‡‚Ó˜IÔ%¬×)AÒ)AÓ)CÌ9×K`ÒK`ÓKbÓ)bÛÙÜ;A¿=¹=Ì&Ï.É.Ñ:YÓZÑ:Y°3”&—-’- Ó-Ó.Ñ:Y€DÐZð$Ûã‰HˆCÜ�MŠM˜#Ö#ó ùò	 [øó ‰HˆCÜ�MŠM˜#Ö#ò üs*   ‚A/C&Á1 B;ÂC&ÂC  Â(C&Ã #C#Ã#C&c          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r@   rM  r   rN  rÀ   r(   r5  rA   úsubprocess.PopenrB   )rC   s   "rD   rE   rE   Ò  s0   € ÷ ñ ˜Dð  tð °.ð ÈDð ÐUeñ rG   c                ól  €  \        VRR7      p\        T4      '       gI   \        P
                  ! T4      P                  p\        P                  ! T4       \        RT RT R	24      h \        P                  ! \        T 4      R
\        T4      .3RTR\        P                  RTR\        P                  /\        P                  ! 4       R8X  d   / MRR/B \        \        4      ;_uu_ 4        \        P                  ! T4       RRR4       #   \         d   p\        RT RT R24      ThRp?ii ; i  + '       g   i     # ; i  \         d   p\        RT 24      ThRp?ii ; i  \        \        4      ;_uu_ 4        \        P                  ! T4       RRR4       i   + '       g   i     i ; i; i)uå   Popen with stdout/stderr appended to ``log_path`` (0o600 from the first byte, O_NOFOLLOW so a planted
symlink e.g. to authorized_keys can't receive output, owner-checked).  Our log fd closes after Popen â€” the child has its dup.FrÀ  z!Refusing to write iron-proxy log ú: ú'.  Remove that path manually and retry.Nziron-proxy log ú has unexpected owner uid=z; refusing to write.z-configrÀ   r¸   rÄ   r    r  Ústart_new_sessionTzfailed to spawn iron-proxy: )r¼  rt   r¢   rã   r­   rß   rá   rÙ   rº   ÚPopenr^   r¼   ÚSTDOUTrb  r…   r   )rM  rN  rÀ   r5  Úlog_fdr‡   Úuids   &&&&   rD   rG  rG  Ò  s‚  € ðBÜ% h¸UÔCˆô ˜6×"Ò"Ü�hŠh�vÓ×%Ñ%ˆÜ
�Š�ÔÜ˜_¨X¨JÐ6PÐQTÐPUÐUiÐjÓkÐkð
ä×ÒÜ�‹]˜I¤s¨3£xÐ0ñ
Ø69ð
ÜAK×ASÑASð
Ø\bð
Üku×k|Ñk|ð
ä—_’_Ó&¨)Ô3‰rÐ:MÈtÐ9Tñ
ô ”g×ÕÜ�HŠH�VÔ÷ Ðøô ô BÜÐ>¸x¸jÈÈ3È%ÐOvÐwÓxð  Bð  	BûðBú÷ ×ûô ô JÜÐ9¸#¸Ð?Ó@ÀcÐIûðJûô ”g×ÕÜ�HŠH�VÔ÷ ××Ðýs`   ‚D Á)A-E Ã-D3ÄD0ÄD+Ä+D0Ä3EÅE%ÅE Å E%Å%E( Å(F3Æ F 	Æ	F3Æ F0Æ+F3c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r@   r7  r^  r%  r^   r(  r9   rA   r/   rB   )rC   s   "rD   rE   rE   ê  s*   € ÷ 
ñ 
Ð+ð 
°3ð 
¸cð 
ÐRVñ 
rG   c               ó  € \         P                   ! 4       \        ,           p V P                  4       e   V! 4       h\        W4      '       d   R# \         P                   ! 4       V8¼  d   R# \         P                  ! R4       Ka  )z…Poll until ``host:port`` accepts or the grace window lapses (do-while: >=1 check even at 0s); raises ``on_exit()`` if the child dies.TFçš™™™™™¹?)ÚtimerL  rK  Ú_port_listeningÚsleep)r7  r%  r(  rD  Údeadlines   &&&$ rD   rJ  rJ  ê  sS   € ä�yŠy‹{Ô3Õ3€HØ
Ø�9‰9‹;Ò"Ù“)ˆOÜ˜4×&Ò&ÙÜ�9Š9‹;˜(Ô"ÙÜ�
Š
�3ŽrG   c               ó$   € V ^8„  d   QhRRRRRR/# )r@   r6  r   r7   r9   rA   rÎ   rB   )rC   s   "rD   rE   rE   ÷  s&   € ÷ ]ñ ] 4ð ]¨cð ]°dñ ]rG   c                ó,  € \         P                  \         P                  ,          \         P                  ,          \        ,          p \         P
                  ! \        V 4      VR4      p \        T4      '       g0   \        RT  R\         P                  ! T4      P                   24      h\         P                   ! T\        T4      P#                  R4      4       \         P$                  ! T4       \&        '       dQ   \)        \        4      ;_uu_ 4        \+        T P-                  R4      \&        P#                  R4      4       R
R
R
4       R
# R
#   \         dm    \        4       ;p'       d$   \        T4      '       d   \        RT  RT R24      hT P                  RR7       \         P
                  ! \        T 4      TR4      p ELZ\         d   p\        RT  RT R	24      ThR
p?ii ; i  \         P$                  ! T4       i ; i  + '       g   i     R
# ; i)z²O_EXCL + O_NOFOLLOW + ownership check, then persist the nonce.  An existing pidfile is either a concurrent
start (fail cleanly) or a stale crash leftover (unlink and retry once).rÐ   z<Another iron-proxy start appears to be in progress (pidfile z -> pid z4).  Run `hermes egress stop` if that proxy is stuck.Trí   zRefusing to write pidfile r`  ra  Nzpidfile rb  r™   z.nonce)r­   rÒ   rÓ   ÚO_EXCLrÕ   rÑ   r^   ÚFileExistsErrorrM  rN  r¢   rñ   rt   rã   rß   rá   rØ   r  rÙ   r,   r   rÛ   rï   )r6  r7   Ú
open_flagsrÚ   Úexisting_pidr‡   s   &&    rD   rH  rH  ÷  s™  € ô —‘œrŸz™zÕ)¬B¯I©IÕ5¼ÕC€JðzÜ�WŠW”S˜“\ :¨uÓ5ˆðÜ˜r×"Ò"Ü ¨'¨Ð2LÌRÏXÊXÐVXË\×M`ÑM`ÐLaÐbÓcÐcÜ
�Š�”S˜“X—_‘_ WÓ-Ô.ä
�Š�Œçƒ|Ü”g×ÕÜ × 3Ñ 3°HÓ =¼|×?RÑ?RÐSZÓ?[Ô\÷ Ññ øô# ô 6Ü%›KÐ'ˆLÖ'¬Z¸×-EÒ-EÜØNÈwÈiÐW_Ð`lÐ_mð  nbð  cóð ð 	�‰ $ˆÔ'Ü�WŠW”S˜“\ :¨uÓ5‹Üô zäÐ7¸°yÀÀ3À%ÐGnÐoÓpÐvyÐyûðzûô 	�Š��ú÷ ×Ðús>   Á!E Á)A/G' Ä/HÅA3G$ÇG$ÇG$ÇGÇG$Ç'G?ÈH	r,  c               ó$   € V ^8„  d   QhRRRRRR/# )r@   r7  r^  r,  r9   rA   rÎ   rB   )rC   s   "rD   rE   rE     s"   € ÷ -ñ -Ð)ð -¸Sð -Èñ -rG   c               óÔ  €  V P                  4         T P                  TR7       R #   \         d     R # i ; i  \        P                   dš    \        \        4      ;_uu_ 4        T P                  4        R R R 4       M  + '       g   i     M; i\        \        P                  4      ;_uu_ 4        T P                  TR7       R R R 4        R #   + '       g   i      R # ; ii ; i)Nr—   )Ú	terminatert   Úwaitrº   rÃ   r   r  )r7  r,  s   &$rD   r3  r3    sŸ   € ðØ�‰Ôð-Ø�	‰	˜-ˆ	Ö(øô ô Úðûô ×$Ñ$ô -Ü”g×ÕØ�I‰IŒK÷ ××Ðúä”j×/Ñ/×0Õ0Ø�I‰I˜mˆIÔ,÷ 1×0×0Ô0úð-úsK   ‚' “9 §6µ6¹,C'Á%A?Á6	C'Á?BÂ
)C'Â3CÃC'ÃC#ÃC'Ã#C'r*  c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r@   r"  r&  r*  r/   r$  r'  rA   r(   rB   )rC   s   "rD   rE   rE   !  s,   € ÷ ñ Ø*ðØKOðØkyðàñrG   c           
     óœ  a
€ \        4       \        P                  upo
\        4       pV Uu0 uF  qUP                  kK  	  ppV Uu/ uF-  qUP
                  '       g   K  VP                  VP
                  bK/  	  ppV FC  pVS
9   d   TM%\        V
3R lVP                  VR4       4       R4      ;p	f   K8  S
V	,          W8&   KE  	  V'       d.   V'       d&   \        W6V\        VP                  R4      4      4       V '       d   VP                  V 4       \         F  pVP                  VR4       K  	  VP                  RR4       V# u upi u upi )a  Allowlisted infra vars + the secrets named in mappings.  With ``refresh_from_bitwarden`` and a populated
``bitwarden_config`` secrets come from BWS (the rotation guarantee); without ``allow_env_fallback`` any BWS
shortfall fails closed instead of keeping stale host-env values.c              3  óZ   <"  € T F   pSP                  V4      '       g   K  Vx € K"  	  R # 5irI   r0  )r2  rá  Úparents   & €rD   r5  Ú._build_proxy_subprocess_env.<locals>.<genexpr>-  s$   øé € Ð7pÑC^¸aÐbh×blÑblÐmn×bo¿ºÓC^ùs   ƒ+¡
+NÚallow_env_fallbackÚNO_COLORÚ1rB   )r°   r­   r®   rÜ  r`   rb   r9  r1  Ú_refresh_secrets_from_bitwardenr/   r¹   r%   r�   Ú
setdefault)r"  r*  r$  rÀ   r‰  r�  ÚneededÚalias_sourcesr¨  r“  r|  s   $$$       @rD   rF  rF  !  s  ø€ ô "Ó#¤R§Z¡Z€K€Cˆä‹€HÙ'/Ó0¡x !�oŒo¡x€FÐ0ÙAIÓ_Á¸A×M^ÕM^Ô7�Q—_‘_ a×&7Ñ&7Ò7Á€MÐ_ÛˆØ" fœn‘d´$Ô7pÀ=×CTÑCTÐUYÐ[]ÔC^Ó7pÐrvÓ2wÐwˆFô  EØ˜v�ˆC‹Iñ ÷ ×"2Ü'¨Ð5EÄtÐL\×L`ÑL`ÐauÓLvÓGwÔxçØ�
‰
�9Ôß+ˆØ�‰��dÖñ ,à‡N�N�:˜sÔ#Ø€Jùò 1ùÚ_s   «EÁE	ÁE	Úcausec          
     ó,   € V ^8„  d   QhRRRRRRRRRR	/# )
r@   r~  r/   rU  r^   r„   r…  zOptional[BaseException]rA   rÎ   rB   )rC   s   "rD   rE   rE   :  s5   € ÷ #ñ #¨Tð #¸#ð #Èð #Ð[rð #ð  Cñ #rG   c               ó|   € V '       g   Ve   \        V4      Vh\        V4      h\        P                  ! V.VO5!   R# )z…Raise ``error`` (chained to ``cause`` when given) unless the operator opted into the legacy host-env fallback (then log ``warning``).N)r¢   rƒ   r„   )r~  rU  r„   r…  Úargss   &&&$*rD   Ú_bitwarden_shortfallr‰  :  s4   € çØÒÜ˜uÓ%¨5Ð0Ü˜5Ó!Ð!Ü
‡N‚N�7Ð"˜TÖ"rG   c          
     ó,   € V ^8„  d   QhRRRRRRRRR	R
/# )r@   rÀ   r(   rƒ  rå  r$  r�  r~  r/   rA   rÎ   rB   )rC   s   "rD   rE   rE   C  s9   € ÷ &Eñ &E¨ð &EÀð &EÐX\ð &EÐrvð &EÐ{ñ &ErG   c           	     ód  a
€  ^ RI Hp \        P                  P	                  VP	                  RR4      R4      P                  4       pVP	                  RR4      pV'       d	   V'       g   \        VRR4       R# VP                  WV^ R	R
7      w  o
p\        T\        S
4      ,
          4      p	T P                  T
3R lT 4       4       T	'       d   \        TRT	 R2RT	4       T'       d"   \        P                  R\        T4      4       R# R#   \         d   p\        TRRYˆR7        Rp?R# Rp?ii ; i)zOverwrite ``env[needed]`` with fresh (uncached) BWS values; only mapped names are injected so unrelated BWS secrets never leak.)Ú	bitwardenÚaccess_token_envÚBWS_ACCESS_TOKENrÁ   Ú
project_idzÓcredential_source=bitwarden but the access-token env or project_id is empty.  Either set both, switch to credential_source: env, or set `proxy.allow_env_fallback: true` to opt into the legacy fallback behaviour.u‰   credential_source=bitwarden but access-token env or project_id is empty â€” proxy will fall back to parent env (allow_env_fallback=true).NF)Úaccess_tokenr�  Úcache_ttl_secondsÚ	use_cachea  Bitwarden refresh module unavailable at proxy start (credential_source=bitwarden with proxy.allow_env_fallback: false).  Either fix the import, switch to credential_source: env, or set `proxy.allow_env_fallback: true` to opt into the legacy fallback behaviour.zmBitwarden refresh module unavailable at proxy start, falling back to parent env (allow_env_fallback=true): %s)r…  c              3  óJ   <"  € T F  qS9   g   K  VSV,          3x € K  	  R # 5irI   rB   )r2  r¯   r¬  s   & €rD   r5  Ú2_refresh_secrets_from_bitwarden.<locals>.<genexpr>_  s   øé € Ð?© 1°w±,Œ��7˜1•:�«ùs   ƒ#�#z-Bitwarden refresh did not return secrets for zÚ.  Either add the secrets to your BWS project, switch to credential_source: env via `hermes egress setup --no-bitwarden`, or set `proxy.allow_env_fallback: true` in config.yaml to opt into the legacy host-env fallback.uw   Bitwarden refresh did not return secrets for %s â€” falling back to host env for those names (allow_env_fallback=true).z[Bitwarden refresh produced %d warning(s); run `hermes secrets bitwarden status` for detail.)Úagent.secret_sourcesrŒ  r­   r®   r1  rÅ   r‰  Úfetch_bitwarden_secretsr  Úsortedrå  r¹   rƒ   r„   r  )rÀ   rƒ  r$  r~  Úbwr�  r�  r=   r‡   Úmissingr¬  s   &&&&      @rD   r�  r�  C  sG  ø€ ðå8Ü—z‘z—~‘~Ð&6×&:Ñ&:Ð;MÐOaÓ&bÐdfÓg×mÑmÓoˆØ%×)Ñ)¨,¸Ó;ˆ
ß§ä Ø"ð}ð \ô	ñ Ø×6Ñ6ÀLÐ{|ð  INÐ6ó  OÑˆ�ô �Vœc '›lÕ*Ó+€GØ‡J�JÔ?©Ó?Ô?ßÜØØ;¸G¸9ð EDð Eð Fð  HOô	
÷ Ü�‰ÐtÔvyð  {Có  wDö  	Eñ øô) ô 	äØðZð |ð  ~Aõ	
õ 	ûð	ús*   ƒAD
 Á!D
 Á)D
 Á8D
 Ä
D/ÄD*Ä*D/c               ó   € V ^8„  d   QhRR/# ©r@   rA   rÎ   rB   )rC   s   "rD   rE   rE   l  s   € ÷ ñ ˜ñ rG   c                 óÚ   € \        4       P                  R R7       \        \        4      ;_uu_ 4        \	        4       P                  4        RRR4       RsR#   + '       g   i     RsR# ; i)Trí   N)rù  rñ   r   rt   r  r,   rB   rG   rD   Ú_forget_daemonr�  l  sG   € äƒJ×Ñ ÐÔ&Ü	”'×	Õ	ÜÓ×&Ñ&Ô(÷ 
à‚L÷ 
Ö	à‚Lús   ²AÁA*	c               ó   € V ^8„  d   QhRR/# r?   rB   )rC   s   "rD   rE   rE   t  s   € ÷ ñ �Dñ rG   c                 ó  € \        4       p V '       d   \        V 4      '       g   \        4        R# \        V 4      p \        P
                  ! V \        P                  4       \        P                  ! 4       R,           p\        P                  ! 4       T8  d+   \        T 4      '       g   M”\        P                  ! R4       KD  \        T 4      pTe   Te   Y8w  g   \        T 4      '       g   \        P                  RT 4       M;\        \        4      ;_uu_ 4        \        P
                  ! T \        4       RRR4       \        4        \        P                  RT 4       R#   \         d    \        4         R# i ; i  + '       g   i     LN; i)zReturns True if it was running.Fg      @rj  Nz?iron-proxy pid=%s appears recycled before SIGKILL; not killing.zStopped iron-proxy pid=%sT)rM  rN  r�  r  r­   r  rV  rX  r  rk  rm  rƒ   r„   r   Ú_KILL_SIGNALr£   )r7   Ústarttime_beforern  Ústarttime_afters       rD   Ú
stop_proxyr£  t  s  € ä
‹+€Cß”j —o’oÜÔÙä*¨3Ó/ÐðÜ
�Š�”V—^‘^Ô$ô
 �yŠy‹{˜SÕ €HÜ
�)Š)‹+˜Ô
 Ü˜#�ŠØÜ�
Š
�3Žä-¨cÓ2ˆØÒ(¨_Ò-HÐM]ÔMpô  zDð  EH÷  zIò  zIÜ�N‰NÐ\Ð^aÕbäÔ,×-Õ-Ü—’˜œ\Ô*÷ .äÔÜ
‡K�KÐ+¨SÔ1Ùøô% ô ÜÔÚðú÷ .×-ús   ¼%E ÄE;ÅE8Å7E8Å;F	c               ó   € V ^8„  d   QhRR/# )r@   rA   r.   rB   )rC   s   "rD   rE   rE   “  s   € ÷ ñ �Kñ rG   c                 óÒ  € \        4       p \        4       w  qn        \        RR7      ;p'       d   V\	        V4      uV n        V n        \        4       pVR,          VR,          rTVP                  4       '       d   TMRVP                  4       '       d   TMRuV n	        V n
        \        4       ;p'       d4   \        V4      '       d#   V\        WP                  4      uV n        V n        V # )zSSnapshot the proxy state without side effects (called per Docker container create).Fr)  r.  ræ   N)r.   rD  r:   rˆ   rÉ   r2   r3   rn   rJ   r4   r5   rM  rN  rl  r7   r8   )rT  rP  r€   rô   rN  Úcar7   s          rD   rE  rE  “  s¾   € ä‹]€FÜ%2£_Ñ"€JÔ"Ü °EÔ:Ð:€vÖ:Ø4:Ô<NÈvÓ<VÐ1ˆÔ˜FÔ1ÜÓ!€EØ�lÕ" E¨HÕ$4ˆØ69·j±j·l²l©sÈÐVX×V_ÑV_×VaÒVaÑPRÐgkÐ+€FÔ˜Ô+Ü‹{ÐˆÖ¤
¨3§¢Ø'*¬O¸J×HZÑHZÓ,[Ð$ˆŒ
�FÔ$Ø€MrG   c               ó$   € V ^8„  d   QhRRRRRR/# )r@   r%  r^   r(  r9   rA   r/   rB   )rC   s   "rD   rE   rE   ¡  s!   € ÷ ñ ˜#ð  Sð ¨Tñ rG   c                ó¨   € ^ RI p VP                  W3RR7      ;_uu_ 4         RRR4       R#   + '       g   i     R# ; i  \         d     R# i ; i)ri   Ng      à?r—   TF)ÚsocketÚcreate_connectionrt   )r%  r(  r©  s   && rD   rl  rl  ¡  sF   € ÛðØ×%Ñ% t l¸CÐ%×@Õ@Ø÷ A×@×@Ò@ûäô Úðús(   †A ¢.¤A ®?	¹A ¿A ÁAÁArƒ  c               ó$   € V ^8„  d   QhRRRRRR/# )r@   rË   r   rƒ  r9   rA   r^   rB   )rC   s   "rD   rE   rE   ª  s!   € ÷ .ñ .�Dð . Cð .°ñ .rG   c               óú   € V P                  4       '       g   R #  RP                  V P                  4       RR P                  RRR7      P	                  4       V) R 4      #   \
         d   pRT R2u Rp?# Rp?ii ; i)	z(no log file)Ú
Nr™   r   rJ  z(could not read log: rÿ  i àÿÿ)rJ   Újoinrò   r¡   ry  rt   )rË   rƒ  r‡   s   &$ rD   r2  r2  ª  sv   € Ø�;‰;�=Š=Ùð.Ø�y‰y˜Ÿ™Ó*¨5¨6Ð2×9Ñ9¸'È)Ð9ÓT×_Ñ_ÓaÐchÐbhÐbiÐjÓkÐkøÜô .Ø& s e¨1Ð-Õ-ûð.ús   šAA ÁA:Á*A5Á/A:Á5A:c               ó   € V ^8„  d   QhRR/# r›  rB   )rC   s   "rD   rE   rE   ³  s   € ÷ ñ ˜$ñ rG   c                 ó2   € \         P                  4        R sR # rI   )r)   Úclearr,   rB   rG   rD   Ú_reset_for_testsr²  ³  s   € ä×ÑÔØ‚LrG   )úopenrouter.aiú*.openrouter.aiúapi.openai.comúapi.anthropic.comú!generativelanguage.googleapis.comúapi.x.aiúapi.mistral.aiúapi.groq.comúapi.together.xyzúapi.deepseek.comúinference.nousresearch.com)r³  r´  )rµ  )rº  )r»  )r¼  )r¹  )r¸  )r½  )r¶  )z	x-api-keyrd   rB   )z*.openai.azure.comz*.cognitiveservices.azure.comz*.services.ai.azure.com)zapi-keyrd   )r·  )zx-goog-api-key)ÚGOOGLE_API_KEY)ÚAWS_ACCESS_KEY_IDÚAWS_SECRET_ACCESS_KEYÚGOOGLE_APPLICATION_CREDENTIALS)z127.0.0.0/8z::1/128z169.254.0.0/16z	fe80::/10z
10.0.0.0/8z172.16.0.0/12z192.168.0.0/16zfc00::/7z::ffff:0:0/96z100.64.0.0/10z198.18.0.0/15)ÚPATHÚHOMEÚTMPDIRÚTZÚLANGÚLC_ALLÚLC_CTYPEr  ÚSSL_CERT_DIRÚSSL_CERT_FILEÚ
SYSTEMROOTÚUSERPROFILE)ÚHTTPS_PROXYÚhttps_proxyÚ
HTTP_PROXYÚ
http_proxyÚ	ALL_PROXYÚ	all_proxyÚNO_PROXYÚno_proxy)zhermes-proxyrI   )r.   r]   r°   r·  rì  rï  rÁ  rû   r  rˆ   rE  r�   rÉ   rÜ  rõ  r  r]  rQ  r£  rÑ  rÉ  )uÚ__conditional_annotations__re   Ú
__future__r   r   rz  rÔ  Úloggingr­   rb  r|   rV  rº   rœ   rS  rk  Úurllib.errorrO  Úurllib.requestÚ
contextlibr   r   Údataclassesr   r   r   Úpathlibr   Útypingr	   r
   r   r   Úutilsr   r   Ú	getLoggerrS   rƒ   rÂ   rL  r²  r±  rS  rX   r   rW   r   r!   r"   r#   r$   r%   ÚgetattrrX  r   rÕ   r)   r  r,   r.   r]   rn   rw   rˆ   r�   rª   r°   rž   rÉ   rÛ   rã   rû   r  r  r  r  r  r)  r:  r?  rD  rX  r]  re  rc  r·  r¼  rÁ  rÉ  rÑ  rÜ  rì  rï  rõ  rù  rM  r  r  r  rN  rQ  rI  rG  rJ  rH  r3  rF  r‰  r�  r�  r£  rE  rl  r2  r²  Ú__all__)rÕ  s   @rD   Ú<module>râ     s   øðôõ #ã Û Û Û Û 	Û Û Û Û Û Û Û Û Û ß /ß 1Ñ 1Ý ß .Ó .ç 6à	×	Ò	˜8Ó	$€à€ØÐ ð 1Ð ØÐ ØÐ ð Ð ð+Ð ˜ó ð Ð>Ð@PÐReØÐ%Ð'9Ð;PØÐ-Ð/@ÐBUØ�= .Ð2Qð	1Ð Ð-ó ð( ˜'Ð#9¸?ÐLjÐluÐwyÐzØ˜WÐ&xØ,Ð.JÈIÐWYð[à�wÐ FÈÐYlÐnwð  zMð  Nð	AÐ Ð=ó ð *zÐ �Ó yð1Ð ˜oó ð4Ð  ó ð
 0\Ð ˜_ó  \ñ �v˜y¨&¯.©.Ó9€á�b˜,¨Ó*€ð "$€�Ó #ð  9Ð Ø"€ˆmÓ "ð ÷yð yó ðyð( ÷*ð *ó ð*õ'õ
ð°%÷ ð$¨÷ õõ.Zð
j¸÷ jõõ	õð E÷ ÷2Iõ
õ6ð
¨U÷ ò$õ
'ðMÈ4÷ M÷Kõjð Ð	kØð  
UðÐ õõ<
(õð(-ØUið-ð HLð-à)-ð-àZ^ð-ð DH÷-õ`
õõõõð"ÈT÷ ð<Èt÷ <ðCÐch÷ Cõ1õ$õ4õ6õ
õ ðF7Ø $ð7ØDHð7Øptð7à#ð7àLQð7àvz÷7ðt ñ
$ó ð
$õõ0
õ]ð8-À1÷ -ðØ-1ðØRWðð }A÷ð2#Ðuy÷ #õ&EõRõõ>õð.¨"÷ .õò‚rG   