Metadata-Version: 2.4
Name: menda-keyring
Version: 0.0.2
Summary: Add your description here
Author-email: jpm2617 <jose.moreno@mendaml.com>
Requires-Python: >=3.11
Requires-Dist: keyring>=25.7.0
Requires-Dist: requests>=2.32.5
Description-Content-Type: text/markdown

# menda-keyring

**menda-keyring** is a [python keyring](https://github.com/jaraco/keyring) backend that supplies short-lived for **menda** package registry credentials for tools like **uv** and **pip**. It does not store passwords locally. Instead, it talks to [menda cloud](https://mendaml.com), which exchanges your **menda** identity (machine or user) for a registry token when a client asks for credentials for a [menda cloud](https://mendaml.com) private registry.

Use it when your private package index is hosted by **menda** and you want installs to authenticate through **menda cloud**.

## Requirements

- Python **3.12+**
- A configured Menda environment (see [Credentials](#credentials))
- Tools that use the standard keyring protocol for HTTP basic auth to private indexes (e.g. **uv** with keyring integration enabled)

## Installation

Install the package into the same environment where you run **uv** or **pip**:

```bash
uv tool install keyring --with menda-keyring
```

The package registers a keyring backend via the `keyring.backends` entry point (`menda-keyring`). After installation, ensure your keyring stack can discover third-party backends `keyring  --list-backends`.

## How it works

1. **uv** or **pip** requests credentials for a URL that matches an AWS CodeArtifact host (e.g. `*.d.codeartifact.*.amazonaws.com`).
2. The keyring calls `MendaCodeArtifactKeyring.get_password(service_url, username)`.
3. The backend resolves [Menda auth](#credentials), obtains a Menda access token, then calls menda-cloud’s `POST /api/v1/auth/registry/token` to receive a **AWS CodeArtifact token** returned as the “password” for that URL.

Non-CodeArtifact URLs are ignored (`get_password` returns `None` so other backends can handle them). **set_password** and **delete_password** are no-ops; this backend never writes secrets to the keychain.

## Configuring your pyproject.toml

Point **uv** at your CodeArtifact **simple** index (or extra index) URL as provided by your menda team. 

Example shape (values are illustrative):

```pyproject.toml
[tool.uv]
keyring-provider = "subprocess"

# PyPI is default so build deps (e.g. hatchling) and public wheels do not go through CodeArtifact.
[[tool.uv.index]]
name = "pypi"
url = "https://pypi.org/simple"
default = true

# Menda SaaS private registry
[[tool.uv.index]]
name = "menda-private"
url = "https://aws@menda-private-058264169513.d.codeartifact.eu-west-1.amazonaws.com/pypi/menda-plugins/simple/"

```

Keep using your normal **uv**/`pip` configuration for index URLs; **menda-keyring** only supplies the credential when the URL is recognized as CodeArtifact.

## Credentials

Auth resolution matches the lightweight logic used `menda-core` package. Tokens are cached under `~/.menda/tokens/cache/.token` where applicable (with expiry checks).

### Resolution order

1. **M2M via environment variables** — if all of these are set, they take precedence:
   - `MENDA_CLIENT_ID`
   - `MENDA_CLIENT_SECRET`
   - `MENDA_HOST` (menda-cloud API base URL, e.g. `https://…`)

   A cached Menda access token may be reused when keyed by `client_id`; otherwise the client performs OAuth2 client-credentials against `MENDA_HOST`, then exchanges for a registry token.

2. **Profile from `~/.menda/mendacfg`** — if the env trio above is not complete, the active profile is loaded (see `MENDA_PROFILE_ID`, default `default`).

   - **`auth_type = m2m`** — requires `host`, `client_id`, and `client_secret` in the profile. Uses the same token flow as (1), with cache keyed by profile id.
   - **`auth_type = u2m`** (default if omitted) — requires `host` and a **valid cached user token** from `menda auth login` (stored in the token cache). If there is no valid cache, you must sign in again or switch to M2M.

### Files and environment variables

| Item | Purpose |
|------|--------|
| `~/.menda/mendacfg` | INI profiles: `host`, `auth_type`, and for M2M `client_id` / `client_secret` |
| `~/.menda/tokens/cache/.token` | Cached tokens (user or M2M), shared with the main Menda CLI where applicable |
| `MENDA_PROFILE_ID` | Which `mendacfg` section to use (default: `default`) |
| `MENDA_CLIENT_ID`, `MENDA_CLIENT_SECRET`, `MENDA_HOST` | Optional: full M2M override without reading a profile |

### Example `mendacfg` snippets

User (device flow, token cache):

```ini
[default]
host = https://api.example.menda.cloud
auth_type = u2m
```

Machine (client credentials in profile):

```ini
[ci]
host = https://api.example.menda.cloud
auth_type = m2m
client_id = <your-client-id>
client_secret = <your-client-secret>
```

Then set `MENDA_PROFILE_ID=ci` in CI if not using the default profile.

## Troubleshooting

| Symptom | What to check |
|--------|----------------|
| “No valid cached token … Run `menda auth login`” | U2M profile without a fresh login, or expired cache. Re-login or use M2M. |
| “Menda config file not found” | Run `menda auth configure` to create `~/.menda/mendacfg`. |
| “No profile …” / “Profile … has no host” | Fix `MENDA_PROFILE_ID` or add the named section with `host = …`. |
| “auth_type=m2m but is missing client_id or client_secret” | Add both fields to the profile or use env-based M2M. |
| HTTP errors from `oauth2/token` or `registry/token` | Network, wrong `MENDA_HOST` / `host`, or invalid credentials; see exception message and optional `status_code` on `MendaCloudAPIError`. |
| Credentials work for PyPI but not private packages | Confirm the index URL is a CodeArtifact URL matching `*.d.codeartifact.*.amazonaws.com` and that keyring is actually invoked by your installer. |

Errors raised by this package are typed under `menda.error.exceptions` (e.g. `MendaCloudAPIError`, `NoCachedLoginError`, `ProfileNotFoundError`).
