
    `gj-                        d Z ddlmZ ddlZddlZddlZddlZddlZddl	Zddl
ZddlmZ ddZddZddZdd		 	 	 	 	 	 	 	 	 	 	 dd
ZddZy)u  ``hermes gateway enroll`` — enroll a self-hosted gateway with a relay connector.

The connector⇄gateway channel is authenticated (the gateway may be
customer-managed and internet-exposed). This command is the gateway half of the
zero-touch enrollment in the connector repo's
``docs/connector-gateway-auth-design.md``:

  1. Resolve a fresh Nous Portal access token from the existing login
     (``~/.hermes/auth.json``) — the same path ``hermes dashboard register``
     uses (``resolve_nous_access_token``). This proves *which Nous org (tenant)*
     the caller owns; the connector derives the authoritative tenant from it via
     ``GET /api/oauth/account`` (never from anything the gateway asserts).
  2. POST ``{enrollmentToken, gatewayId}`` to the connector's ``/relay/enroll``
     with that token in the ``Authorization`` header, over TLS.
  3. The connector verifies the enrollment token (signature + single-use +
     tenant match), mints a per-gateway secret, get-or-creates the per-tenant
     delivery key, and returns both ONCE.
  4. Persist ``GATEWAY_RELAY_ID`` / ``GATEWAY_RELAY_SECRET`` /
     ``GATEWAY_RELAY_DELIVERY_KEY`` (+ ``GATEWAY_RELAY_URL`` if supplied) into
     ``~/.hermes/.env``. The per-gateway secret authenticates the WS upgrade;
     the per-tenant delivery key verifies signed inbound deliveries.

Managed/hosted installs do NOT self-enroll: the orchestrator (NAS) mints the
secret directly and stamps it into the container env, so this command refuses to
run under ``is_managed()`` (mirrors ``dashboard register``).

EXPERIMENTAL: the relay auth scheme may change without a deprecation cycle until
≥2 Class-1 platforms validate the contract.
    )annotationsN)Optionalc                     d} 	 t        j                         j                         } d| xs d S # t        $ r d} Y w xY w)a5  A stable-ish default gateway instance id: ``<hostname>-<pid-free slug>``.

    The gatewayId identifies this enrolled instance for kill-switch granularity
    (the connector indexes its secret verify list by it). Default to the host
    name so a human can recognize it; overridable via ``--gateway-id``.
     zgw-hermes)socketgethostnamestrip	Exception)hosts    L/root/.hermes/venv/lib/python3.12/site-packages/hermes_cli/gateway_enroll.py_default_gateway_idr   +   sP     D!!#))+ !"##  s   "/ ==c                   | xs  t         j                  j                  dd      j                         }|sN	 ddlm}  |       j                  d      xs i }t        |j                  dd      xs d      j                         }|sy|j                  d      }|j                  d	      rd
|t        d	      d z   }n"|j                  d      rd|t        d      d z   }|j                  d      r|dt        d        }|S # t        $ r d}Y w xY w)a  Resolve the connector base URL (no trailing slash) for enrollment.

    Precedence: explicit ``--connector-url`` flag > ``GATEWAY_RELAY_URL`` env >
    ``gateway.relay_url`` in config.yaml. The relay URL is a ``ws(s)://`` dial
    target; enrollment is an ``http(s)://`` POST to the same host, so we map the
    scheme. Returns None when nothing is configured (the user must supply one).
    GATEWAY_RELAY_URLr   r   )_load_gateway_configgateway	relay_urlN/zws://zhttp://zwss://zhttps://z/relay)osenvirongetr
   gateway.runr   strr   rstrip
startswithlenendswith)overriderawr   cfgs       r   _resolve_connector_urlr!   :   s     >rzz~~&92>
E
E
GC	8')--i8>BCcggk2.4"5;;=C 
**S/C
~~g#c'lm,,		!3s8}~..
||H"S]N#J  	C	s   AC> >DDc                     ddl m}   |        S )u  Resolve the caller-identity bearer token (generic-OIDC or Nous Portal).

    Delegates to the canonical resolver in ``gateway.relay`` so the enroll CLI and
    the runtime self-provision path share ONE implementation (generic OAuth2
    client-credentials when ``gateway.idp.token_url`` is set — the air-gapped /
    self-hosted-IdP path; otherwise Nous Portal). Raises RuntimeError on failure.
    r   _resolve_relay_identity_token)gateway.relayr$   r#   s    r   _resolve_identity_tokenr&   Y   s     <(**    g      .@timeoutc           	     2   | j                  d       d}t        j                  ||d      j                  d      }t        j
                  j                  ||dd| ddd	      }	 t        j
                  j                  ||
      5 }t        j                  |j                         j                               }	ddd       t'        	t(              r|	j                  d      st!        d      |	S # 1 sw Y   7xY w# t        j                  j                  $ r}
d}	 t        j                  |
j                         j                               xs i j                  dd      }n# t        $ r Y nw xY w|
j                  dk(  rt!        d      |
|
j                  dk(  rt!        |xs d      |
t!        d|
j                   |rd| z         |
dz         |
d}
~
wt        j                  j"                  $ r!}
t!        d|  d|
j$                         |
d}
~
ww xY w)a  POST to the connector's ``/relay/enroll`` and return the JSON body.

    Raises RuntimeError with a user-facing message on any non-2xx / transport
    failure. The connector returns ``{secret, deliveryKey, tenant, gatewayId}``
    on success, ``{error}`` at 400/401/403.
    r   z/relay/enroll)enrollmentToken	gatewayIdzutf-8POSTzBearer zapplication/json)AuthorizationzContent-TypeAccept)datamethodheadersr(   Nr   errori  u   Connector rejected the caller identity (401). Your Nous Portal token could not be verified — try `hermes auth add nous` and retry.i  zJEnrollment token invalid, expired, already used, or tenant mismatch (403).zConnector returned HTTP z: z!Could not reach the connector at secretz6Connector returned an unexpected response (no secret).)r   jsondumpsencodeurllibrequestRequesturlopenloadsreaddecoder3   	HTTPErrorr   r   codeRuntimeErrorURLErrorreason
isinstancedict)connector_base_urlaccess_tokenenrollment_token
gateway_idr)   urlr0   reqresppayloadexcdetails               r   _post_enrollrP   g   s     &&s+,M
:C::*:TU\\]deD
..
 
 &|n5.(
	 ! 	C^^##C#9 	7Tjj!3!3!56G	74 gt$GKK,ASTTN9	7 	7<<!! 	jj!2!2!45;@@"MF 		88s?X  88s? `_  &sxxj1fr&]U
	RTU
	 <<   /0B/C2cjj\R
	sb   '!C< 2C0:C< 0C95C< <HGAE"!G"	E.+G-E..A'G H5HHc                   ddl m}m} ddlm}m}  |       r t        d       t        j                  d       t        | dd      xs  t        j                  j                  dd	      j                         }|s t        d
       t        j                  d       t        t        | dd            }|s t        d       t        j                  d       t        | dd      xs
 t               j                         }	 t!               }	 t%        |||      }
t)        
j                  d      xs d	      }t)        |
j                  d      xs d	      }t)        |
j                  d      xs d	      }t)        |
j                  d      xs |      }|||d}t        | dd      xs d	j                         }|r|j+                  d      |d<   t        | dd      xs d	j                         }|r|j+                  d      |d<   |j-                         D ]  \  }}|s		  |||        dd!lm} t        d"| d#|rd$| nd	z          t                t        d% |        d&       t        d'|        t        d(       t        d)       |rt        d*|j+                  d              |rt        d+|j+                  d              t                t        d,       y# |$ rR}	t        |	dd      rt        d       t        d       nt        d|	        t        j                  d       Y d}	~	$d}	~	wt"        $ r.}	t        d|	        t        j                  d       Y d}	~	Yd}	~	ww xY w# t&        $ r.}	t        d|	        t        j                  d       Y d}	~	d}	~	ww xY w# t"        $ r1}	t        d| d |	        t        j                  d       Y d}	~	d}	~	ww xY w)-zKEnroll this gateway with a relay connector; persist the auth creds to .env.r   )	AuthErrorresolve_nous_access_token)
is_managedsave_env_valueu   ✗ `hermes gateway enroll` is not available in a managed/hosted install.
  The relay gateway secret is provisioned by the hosting platform.   tokenNGATEWAY_RELAY_ENROLL_TOKENr   u   ✗ No enrollment token. Pass --token <token> (or set GATEWAY_RELAY_ENROLL_TOKEN).
  The connector mints this single-use token when your tenant's route is provisioned; it is delivered with your gateway config.connector_urluo   ✗ No connector URL. Pass --connector-url <url> (or set GATEWAY_RELAY_URL / gateway.relay_url in config.yaml).rI   relogin_requiredFu'   ✗ You're not logged into Nous Portal.zC  Run `hermes setup` (or `hermes auth add nous`) first, then retry.u2   ✗ Could not resolve a Nous Portal access token: u/   ✗ Could not resolve a caller-identity token: )rF   rG   rH   rI   u   ✗ Enrollment failed: r4   deliveryKeytenantr,   )GATEWAY_RELAY_IDGATEWAY_RELAY_SECRETGATEWAY_RELAY_DELIVERY_KEYr   r   wake_urlGATEWAY_RELAY_WAKE_URLu   ✗ Failed to write z
 to .env: )get_env_pathu   ✓ Enrolled gateway ""z for tenant z  Wrote to :z    GATEWAY_RELAY_ID=z!    GATEWAY_RELAY_SECRET=<hidden>z'    GATEWAY_RELAY_DELIVERY_KEY=<hidden>z    GATEWAY_RELAY_URL=z    GATEWAY_RELAY_WAKE_URL=z  The gateway now authenticates its relay WS upgrade with the per-gateway
  secret and verifies signed inbound deliveries with the tenant delivery
  key. Restart the gateway to pick up the new env.)hermes_cli.authrR   rS   hermes_cli.configrT   rU   printsysexitgetattrr   r   r   r
   r!   r   r&   r   rP   rA   r   r   itemsrb   )argsrR   rS   rT   rU   rH   rF   rI   rG   rN   resultr4   delivery_keyr\   resolved_gateway_idto_writeexplicit_urlexplicit_wake_urlkeyvaluerb   s                        r   cmd_gateway_enrollru      s   D< |Q	
 	gt4h

Gceg8hooqH	
 	/ot0TU3	
 	$d3L7J7LSSUJ
.0	1%-!	
 H%+,Fvzz-06B7LH%+,Ffjj5CD
 0 &&2H D/48>BEEGL(4(;(;C(@$% !z48>BEEG->-E-Ec-J)*nn& 
U	3&	 /	"#6"7q
9X^|F8=Tdf
gh	G	K'q
)*	!"5!6
78	
-.	
34&|':':3'?&@AB+,=,D,DS,I+JKL	G		=O  3*E2;<WXFseLM ?uEF  'u-.F  	(Zu=>HHQKK	sU   7
K M" 	NMAL%%M1#MM"	N+#NN	O%&OO)returnr   )r   Optional[str]rv   rw   )rF   r   rG   r   rH   r   rI   r   r)   floatrv   rE   )rv   None)__doc__
__future__r   r5   r   r   rh   urllib.errorr8   urllib.parseurllib.requesttypingr   r   r!   r&   rP   ru    r'   r   <module>r      s   < #  	  
    $>
+( 77 7 	7
 7 7 
7ttr'   