flip_api.scripts.register_trust =============================== .. py:module:: flip_api.scripts.register_trust .. autoapi-nested-parse:: Register a single trust on the hub and emit its kit (deploy-time CLI). Invoked once per trust by the deploy Makefile's ``register-trust-`` targets (dev: ``docker compose exec``; prod: a one-off ECS task). The trust's name / code / region are passed as arguments — the hub carries no trust list of its own; the deploy tooling decides what to register. Idempotent: if a trust with ``--name`` already exists it is skipped. Output transports — two contracts depending on ``--out-ssm-parameter``: - *Default (dev pipe / docker compose exec)* — the kit JSON array prints on **stdout**. Consumed locally by ``scripts/distribute_trust_kits.py``. The pipe is local: no remote storage involved. - *``--out-ssm-parameter `` (prod ECS one-off task)* — the kit JSON is written to an SSM Parameter Store SecureString at ````; **stdout** receives only that parameter name (one ASCII line, safe to capture in CloudWatch). The deploy script then ``ssm:GetParameter --with-decryption`` reads the kit out-of-band and ``ssm:DeleteParameter`` immediately deletes it. This is the S-1 fix: it keeps plaintext API keys, internal-service keys, and ``hub_shared.AES_KEY_BASE64`` out of CloudWatch entirely (the awslogs driver only sees the parameter name, never the kit body). Kit shape (both transports): - *New registration*: full kit (``trust_id``, ``trust_name``, ``trust_api_key``, ``trust_internal_service_key``, ``fl_kit_slot``, ``fl_kit_slot_number``, ``hub_shared``). - *Idempotent skip* (trust already existed): metadata-only kit (same keys minus ``trust_api_key`` / ``trust_internal_service_key``). The hub stores only the SHA-256 hashes of those keys and cannot re-emit plaintext, so credentials are intentionally absent on the skip path. Both shapes include ``hub_shared`` so the deploy distributor can sync shared env values without rotating credentials. - **stderr** — human-readable logging (does not contain the kit body). - Exit code 0 on success or skip; 1 on a registration or transport failure. Attributes ---------- .. autoapisummary:: flip_api.scripts.register_trust.HUB_SHARED_ENV_KEYS Functions --------- .. autoapisummary:: flip_api.scripts.register_trust._hub_shared_from_env flip_api.scripts.register_trust.register_one_trust flip_api.scripts.register_trust._write_kit_to_ssm flip_api.scripts.register_trust.main Module Contents --------------- .. py:data:: HUB_SHARED_ENV_KEYS :value: ('AES_KEY_BASE64', 'CENTRAL_HUB_API_URL', 'TRUST_API_KEY_HEADER', 'FL_BACKEND',... .. py:function:: _hub_shared_from_env() -> dict[str, str] Read hub-shared values from os.environ. Unset keys are omitted (no empty strings). .. py:function:: register_one_trust(name: str, code: str | None, region: str | None, session: sqlmodel.Session, require_existing: bool = False) -> list[dict[str, Any]] Register one trust if it does not already exist. :param name: Trust display name. :type name: str :param code: Short code (e.g. ``GSTT``). Required when registering a new trust — ``register_trust`` raises ``EmptyTrustCodeError`` on a blank code. Unused on the idempotent-skip / ``require_existing`` paths. :type code: str | None :param region: Optional NHS region. :type region: str | None :param session: SQLModel session. :type session: Session :param require_existing: When True, exit with an error if the trust has not yet been registered. Prevents ``sync-trust-kit-N`` from silently minting and discarding credentials for an unregistered trust. :type require_existing: bool :returns: ``[kit]`` always — one full kit dict (including credentials) for a new registration, or one metadata-only dict (no credentials) when the trust already existed (idempotent skip). Both shapes include a ``hub_shared`` key populated from os.environ so the deploy distributor can sync shared values without rotating credentials. :rtype: list[dict[str, Any]] :raises TrustRegistrationError: If registration of a new trust fails. :raises SystemExit: If ``require_existing`` is True and the trust does not exist. .. py:function:: _write_kit_to_ssm(kits: list[dict[str, Any]], parameter_name: str) -> None Write the kit JSON to an SSM Parameter Store SecureString. The deploy script (``register-trusts.sh``) mints the parameter name before spawning this CLI as a one-off ECS task, then reads + deletes it after the task completes. The ECS task role only needs ``ssm:PutParameter`` on this path; SecureString encryption uses the AWS-managed ``alias/aws/ssm`` so no extra KMS permissions are required. ``Overwrite=True`` is intentional — re-runs of ``register-trusts.sh`` may reuse a parameter name on retry. Idempotent registration emits the same metadata-only kit, so overwriting is safe. :param kits: The kit list to persist (always length 1). :type kits: list[dict[str, Any]] :param parameter_name: SSM parameter name (must start with ``/``). :type parameter_name: str :raises SystemExit: If the SSM put fails — the deploy script must NOT proceed on a partial write, since the credentials block in ``kits`` is unrecoverable from the hub side once the session is committed. .. py:function:: main() -> None CLI entry point: register one trust, emit its kit JSON to the chosen output.