+
    jHàj   ã                  ó$  a € 0 t $ R t^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIH	t	 ^ RI
Ht ^ RIHtHtHt ^ RIHtHtHtHt ]P*                  ! ]4      tRt^+t^tRtR	tR
sR]R&   R R ltR]/R R llt  ! R R]]4      t!R R lt"R R lt#R R lt$R# )ur  DrainSecretProvider â€” shared-bearer-secret auth for the drain-control endpoint.

Non-interactive token capability of the ``DashboardAuthProvider`` ABC (``verify_token`` +
the ``token_auth`` middleware seam): ``nous-account-service`` provisions a per-agent unique
secret (``HERMES_DASHBOARD_DRAIN_SECRET``, env-only â€” it is a credential); an inbound bearer
is compared constant-time and vouched for as the ``drain-control`` principal. Fail-CLOSED
entropy gate at registration (length, distinct chars, Shannon bits); interactive ABC methods
raise. Knobs ``scope`` / ``min_secret_chars`` live under ``dashboard.drain_auth``.
)ÚannotationsN)ÚCounter)ÚOptional)ÚDashboardAuthProviderÚSessionÚTokenPrincipal)ÚNonInteractiveMixinÚSkipRegistrationÚload_config_sectionÚregister_providerzdashboard-auth-draing      `@z/api/gateway/drainÚ ÚstrÚLAST_SKIP_REASONc               ó    € V ^8„  d   QhRRRR/# )é   Úvaluer   ÚreturnÚfloat© )Úformats   "ÚJ/home/ubuntu/.hermes/hermes-agent/plugins/dashboard_auth/drain/__init__.pyÚ__annotate__r   '   s   € ÷ ñ ˜ð  ñ ó    c                óš   a€ V '       g   R# \        V 4      o\        V3R l\        V 4      P                  4        4       4      ) pVS,          # )zJTotal Shannon entropy (bits) of ``value`` over its character distribution.g        c              3  óx   <"  € T F/  qS,          \         P                  ! VS,          4      ,          x € K1  	  R # 5i©N)ÚmathÚlog2)Ú.0ÚcÚns   & €r   Ú	<genexpr>Ú _shannon_bits.<locals>.<genexpr>,   s*   øé € ÐPÑ8O°1˜•UœdŸiši¨¨A­Ó.×.Ò.Ó8Oùs   ƒ7:)ÚlenÚsumr   Úvalues)r   Úper_charr    s   & @r   Ú_shannon_bitsr'   '   s<   ø€ çÙÜˆE‹
€AÜÔP¼À»×8MÑ8MÔ8OÓPÓPÐP€HØ�a�<Ðr   Ú	min_charsc               ó$   € V ^8„  d   QhRRRRRR/# )r   Úsecretr   r(   Úintr   zOptional[str]r   )r   s   "r   r   r   0   s"   € ÷ ñ  3ð °cð ÐZgñ r   c               ó   € V '       g   R# \        V 4      V8  d   R\        V 4       RV R2# \        \        V 4      4      pV\        8  d   RV R\         R2# \        V 4      pV\        8  d   RVR	 R
\        R	 R2# R# )zÉHuman-readable rejection reason if ``secret`` is too weak, else ``None``. Checks, in
order: length >= ``min_chars``, distinct chars >= ``_MIN_DISTINCT_CHARS``, Shannon
entropy >= ``_MIN_SHANNON_BITS``.zsecret is emptyzsecret too short: z chars (need >= z]; use a >=256-bit value, e.g. `python -c "import secrets; print(secrets.token_urlsafe(32))"`)zsecret has only z distinct characters (need >= z); looks structured/low-entropyzsecret entropy too low: z.0fz bits (need >= z); looks structured/repeatedN)r#   ÚsetÚ_MIN_DISTINCT_CHARSr'   Ú_MIN_SHANNON_BITS)r*   r(   ÚdistinctÚbitss   &$  r   Úassess_secret_strengthr2   0   s�   € ÷ Ù Ü
ˆ6ƒ{�YÔà ¤ V£ Ð-=¸i¸[ð I3ð 3ð	4ô ”3�v“;Ó€HØÔ%Ô%Ø! ( Ð+IÔJ]ÐI^Ð^}Ð~Ð~Ü˜Ó €DØÔÔØ)¨$¨s¨°?ÔCTÐUXÐBYÐYuÐvÐvÙr   c                  óv   € ] tR t^DtRtRtRtRtRtRt	Rt
RR	/R
 R lltR R ltR R ltR R ltR R ltRtR# )ÚDrainSecretProviderz@Non-interactive shared-bearer-secret provider for drain control.zdrain-secretz"Drain Control (service credential)TFz<DrainSecretProvider is a non-interactive service credential.zTDrainSecretProvider is a non-interactive service credential; there is no login flow.ÚscopeÚdrainc               ó$   € V ^8„  d   QhRRRRRR/# )r   r*   r   r5   r   ÚNoner   )r   s   "r   r   Ú DrainSecretProvider.__annotate__N   s!   € ÷ 'ñ ' #ð '¨cð 'Àñ 'r   c               	ón   € \        V4      pVe   \        RV 24      hWn        T;'       g    RV n        R # )Nzdrain secret rejected: r6   )r2   Ú
ValueErrorÚ_secretÚ_scope)Úselfr*   r5   Úreasons   &$$ r   Ú__init__ÚDrainSecretProvider.__init__N   s;   € ô (¨Ó/ˆØÒÜÐ6°v°hÐ?Ó@Ð@ØŒØ×&Ð&˜wˆŽr   c               ó    € V ^8„  d   QhRRRR/# )r   Útokenr   r   zOptional[TokenPrincipal]r   )r   s   "r   r   r9   Y   s   € ÷ ñ  Sð Ð-Eñ r   c               óæ   € V'       di   \         P                  ! VP                  R4      V P                  P                  R4      4      '       d$   \	        RV P
                  V P                  3R7      # R# )z|Constant-time compare; ``drain-control`` principal on match, else
``None`` so the generic seam falls through / fails closed.zutf-8zdrain-control)Ú	principalÚproviderÚscopesN)ÚhmacÚcompare_digestÚencoder<   r   Únamer=   )r>   rC   s   &$r   Úverify_tokenÚ DrainSecretProvider.verify_tokenY   sR   € ÷ ”T×(Ò(¨¯©°gÓ)>ÀÇÁ×@SÑ@SÐT[Ó@\×]Ò]Ü!¨OÀdÇiÁiÐY]×YdÑYdÐXfÔgÐgÙr   c               ó    € V ^8„  d   QhRRRR/# )r   Úaccess_tokenr   r   zOptional[Session]r   )r   s   "r   r   r9   b   s   € ÷ ñ ¨cð Ð6Gñ r   c               	ó   € R # r   r   )r>   rO   s   &$r   Úverify_sessionÚ"DrainSecretProvider.verify_sessionb   s   € ñ r   c               ó    € V ^8„  d   QhRRRR/# )r   Úrefresh_tokenr   r   r   r   )r   s   "r   r   r9   g   s   € ÷ 9ñ 9°ð 9¸ñ 9r   c               	ó,   € \        V P                  4      hr   )ÚNotImplementedErrorÚ_NOT_INTERACTIVE©r>   rT   s   &$r   Úrefresh_sessionÚ#DrainSecretProvider.refresh_sessiong   s   € Ü! $×"7Ñ"7Ó8Ð8r   c               ó    € V ^8„  d   QhRRRR/# )r   rT   r   r   r8   r   )r   s   "r   r   r9   j   s   € ÷ ñ ¨sð °tñ r   c               	ó   € R # r   r   rX   s   &$r   Úrevoke_sessionÚ"DrainSecretProvider.revoke_sessionj   s   € Ùr   )r=   r<   N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__rK   Údisplay_nameÚsupports_tokenÚsupports_sessionrW   Ú_NO_START_LOGINr@   rL   rQ   rY   r]   Ú__static_attributes__r   r   r   r4   r4   D   sJ   † ÙJà€DØ7€LØ€NØÐØUÐØl€Oð'°G÷ 'õõõ
9÷ñ r   r4   c               ó   € V ^8„  d   QhRR/# ©r   r   Údictr   )r   s   "r   r   r   p   s   € ÷ Hñ H¨ñ Hr   c                 ó.   € \        \        \        R R4      # )Ú	dashboardÚ
drain_auth)r
   ÚloggerÚ_TAGr   r   r   Ú_load_config_drain_auth_sectionrq   p   s   € Üœv¤t¨[¸,ÓGÐGr   c               ó   € V ^8„  d   QhRR/# rj   r   )r   s   "r   r   r   t   s   € ÷ .ñ .�4ñ .r   c                 óÚ  € \         P                  P                  RR4      P                  4       p V '       g   \	        R4      h\        4       p\        VP                  RR4      ;'       g    R4      P                  4       ;'       g    Rp \        VP                  R\        4      4      p\        WR7      pVe   \	        RV R	2R
R7      hRV RV/#   \        \        3 d
    \        p L@i ; i)zPResolve DrainSecretProvider kwargs from env/config; raises ``SkipRegistration``.ÚHERMES_DASHBOARD_DRAIN_SECRETr   zçHERMES_DASHBOARD_DRAIN_SECRET is not set. Set a per-agent >=256-bit secret (e.g. `python -c "import secrets; print(secrets.token_urlsafe(32))"`) to enable NAS-driven drain coordination; leave it unset to disable the drain endpoint.r5   r6   Úmin_secret_chars)r(   u+   HERMES_DASHBOARD_DRAIN_SECRET rejected â€” z2. The drain endpoint stays disabled (fail-closed).Úwarning)Úlevelr*   )ÚosÚenvironÚgetÚstripr	   rq   r   r+   Ú_DEFAULT_MIN_SECRET_CHARSÚ	TypeErrorr;   r2   )r*   Úsectionr5   r(   r?   s        r   Ú	_settingsr   t   sç   € ä�Z‰Z�^‰^Ð;¸RÓ@×FÑFÓH€FßÜð[ó\ð 	\ô .Ó/€GÜ�—‘˜G WÓ-×8Ð8°Ó9×?Ñ?ÓA×LÐLÀW€Eð.Ü˜Ÿ™Ð$6Ô8QÓRÓSˆ	ô $ FÔ@€FØÒÜØ9¸&¸ÐAsÐtØôð 	ð �f˜g uÐ-Ð-øô ”zÐ"ô .Ü-Š	ð.ús   Â
C ÃC*Ã)C*c               ó   € V ^8„  d   QhRR/# )r   r   r8   r   )r   s   "r   r   r   Š   s   € ÷ +ñ +�Tñ +r   c                ó*  € Rs \        V \        \        \        \
        4      w  ps Vf   R#  ^ RIHp V! \        4       \        P                  RVR,          \        4       R#   \         d&   p\        P                  R\        T4        Rp?LORp?ii ; i)zøRegister ``DrainSecretProvider`` when a strong secret is set; no-op (records a skip
reason) when ``HERMES_DASHBOARD_DRAIN_SECRET`` is unset or fails the entropy gate. On
success also registers the drain route as token-authable via the generic seam.r   N)Úregister_token_routez;dashboard-auth-drain: could not register token route %s: %szWdashboard-auth-drain: registered drain service-credential provider (scope=%s, route=%s)r5   )r   r   ro   rp   r4   r   Ú$hermes_cli.dashboard_auth.token_authr‚   ÚDRAIN_ROUTE_PATHÚ	Exceptionrv   Úinfo)ÚctxÚkwargsr‚   Úexcs   &   r   ÚregisterrŠ   Š   sƒ   € ð
 ÐÜ0°´f¼dÔDWÔYbÓcÑ€FÐØ‚~ÙðmÝMáÔ-Ô.ô ‡K�KØaØˆw�Ô)ö+øô ô mÜ�‰ÐTÔVfÐhk×lÑlûðmús   ¬A" Á"BÁ-BÂB)%Ú__conditional_annotations__rc   Ú
__future__r   rH   Úloggingr   rx   Úcollectionsr   Útypingr   Úhermes_cli.dashboard_authr   r   r   Úplugins.dashboard_auth._sharedr   r	   r
   r   Ú	getLoggerr_   ro   rp   r|   r.   r/   r„   r   Ú__annotations__r'   r2   r4   rq   r   rŠ   )r‹   s   @r   Ú<module>r”      s�   øðôõ #ã Û Û Û 	Ý Ý ç TÑ Tß xÓ xà	×	Ò	˜8Ó	$€Ø€ð Ð àÐ àÐ ð (Ð àÐ �#Ó õðÐ<U÷ ô('Ð-Ð/Dô 'õXHõ.÷,+r   