> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/nemo/gym/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/nemo/gym/_mcp/server.

# nemo_gym.sandbox.attribution

Automatic job attribution (team / user / workload / run) for sandbox metadata.

Sandbox providers merge these keys into every sandbox's metadata so cluster
operators can attribute running sandboxes to the team, user, and workload that
created them (on OpenSandbox, metadata becomes queryable Kubernetes labels on
the sandbox). Each field resolves from explicit configuration first, then
`NEMO_GYM_*` environment variables, then Slurm job environment variables,
then (for `user` only) the OS login name and (for `workload`) the server
instance's config path. Fields that cannot be resolved are omitted rather than
guessed.

`run` identifies one launch of the creating process so a run's sandboxes can
be listed or garbage-collected exactly, even when the same user runs the same
workload twice. It resolves from explicit configuration, then `NEMO_GYM_RUN_ID`,
then a per-process generated id.

## Module Contents

### Functions

| Name                                                                         | Description                                                               |
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| [`_detect_user`](#nemo_gym-sandbox-attribution-_detect_user)                 | -                                                                         |
| [`_first_env`](#nemo_gym-sandbox-attribution-_first_env)                     | -                                                                         |
| [`log_attribution_once`](#nemo_gym-sandbox-attribution-log_attribution_once) | Log the resolved attribution metadata once per process.                   |
| [`resolve_attribution`](#nemo_gym-sandbox-attribution-resolve_attribution)   | Resolve `team` / `user` / `workload` attribution metadata.                |
| [`resolve_run_id`](#nemo_gym-sandbox-attribution-resolve_run_id)             | Resolve the `run` attribution id: explicit value, then `NEMO_GYM_RUN_ID`, |

### Data

[`IGNORED_LOGIN_NAMES`](#nemo_gym-sandbox-attribution-IGNORED_LOGIN_NAMES)

[`LOGGER`](#nemo_gym-sandbox-attribution-LOGGER)

[`RUN_ENV_VARS`](#nemo_gym-sandbox-attribution-RUN_ENV_VARS)

[`RUN_KEY`](#nemo_gym-sandbox-attribution-RUN_KEY)

[`TEAM_ENV_VARS`](#nemo_gym-sandbox-attribution-TEAM_ENV_VARS)

[`TEAM_KEY`](#nemo_gym-sandbox-attribution-TEAM_KEY)

[`USER_ENV_VARS`](#nemo_gym-sandbox-attribution-USER_ENV_VARS)

[`USER_KEY`](#nemo_gym-sandbox-attribution-USER_KEY)

[`WORKLOAD_ENV_VARS`](#nemo_gym-sandbox-attribution-WORKLOAD_ENV_VARS)

[`WORKLOAD_KEY`](#nemo_gym-sandbox-attribution-WORKLOAD_KEY)

[`_logged_attribution`](#nemo_gym-sandbox-attribution-_logged_attribution)

[`_process_run_id`](#nemo_gym-sandbox-attribution-_process_run_id)

### API

```python
nemo_gym.sandbox.attribution._detect_user(
    environ: collections.abc.Mapping[str, str]
) -> str | None
```

```python
nemo_gym.sandbox.attribution._first_env(
    environ: collections.abc.Mapping[str, str],
    names: tuple[str, ...]
) -> str | None
```

```python
nemo_gym.sandbox.attribution.log_attribution_once(
    metadata: collections.abc.Mapping[str, str]
) -> None
```

Log the resolved attribution metadata once per process.

The generated `run` id only exists in this process, so surfacing it in the logs
is what lets operators later filter or garbage-collect this run's sandboxes.

```python
nemo_gym.sandbox.attribution.resolve_attribution(
    team: str | None = None,
    user: str | None = None,
    workload: str | None = None,
    environ: collections.abc.Mapping[str, str] | None = None
) -> dict[str, str]
```

Resolve `team` / `user` / `workload` attribution metadata.

**Parameters:**

**`team`** `str | None` — default: None

Explicit team; falls back to `NEMO_GYM_TEAM`, then `SLURM_JOB_ACCOUNT`.

---

**`user`** `str | None` — default: None

Explicit user; falls back to `NEMO_GYM_USER`, then `SLURM_JOB_USER`,
then the OS login name (`root` is ignored — containers run as root by
default, so it attributes the image, not a person).

---

**`workload`** `str | None` — default: None

Explicit workload; falls back to `NEMO_GYM_WORKLOAD`, then
`SLURM_JOB_NAME`, then `NEMO_GYM_CONFIG_PATH` (the server instance
name the gym CLI sets on every server process it spawns).

---

**`environ`** `Mapping[str, str] | None` — default: None

Environment mapping override, for testing. Defaults to `os.environ`.

---

**Returns:** `dict[str, str]`

A dict with only the resolved keys among `team`, `user`, and `workload`.

```python
nemo_gym.sandbox.attribution.resolve_run_id(
    run: str | None = None,
    environ: collections.abc.Mapping[str, str] | None = None
) -> str
```

Resolve the `run` attribution id: explicit value, then `NEMO_GYM_RUN_ID`,
then an id generated once per process.

Unlike :func:`resolve_attribution` fields, `run` is always resolvable. It scopes
sandboxes to one launch of the creating process, so an interrupted run's sandboxes
can be listed and cleaned up exactly (`team` / `user` / `workload` cannot
distinguish two runs of the same workload by the same user).

```python
nemo_gym.sandbox.attribution.IGNORED_LOGIN_NAMES = frozenset({'root'})
```

```python
nemo_gym.sandbox.attribution.LOGGER = logging.getLogger(__name__)
```

```python
nemo_gym.sandbox.attribution.RUN_ENV_VARS = ('NEMO_GYM_RUN_ID',)
```

```python
nemo_gym.sandbox.attribution.RUN_KEY = 'run'
```

```python
nemo_gym.sandbox.attribution.TEAM_ENV_VARS = ('NEMO_GYM_TEAM', 'SLURM_JOB_ACCOUNT')
```

```python
nemo_gym.sandbox.attribution.TEAM_KEY = 'team'
```

```python
nemo_gym.sandbox.attribution.USER_ENV_VARS = ('NEMO_GYM_USER', 'SLURM_JOB_USER')
```

```python
nemo_gym.sandbox.attribution.USER_KEY = 'user'
```

```python
nemo_gym.sandbox.attribution.WORKLOAD_ENV_VARS = ('NEMO_GYM_WORKLOAD', 'SLURM_JOB_NAME', 'NEMO_GYM_CONFIG_PATH')
```

```python
nemo_gym.sandbox.attribution.WORKLOAD_KEY = 'workload'
```

```python
nemo_gym.sandbox.attribution._logged_attribution = False
```

```python
nemo_gym.sandbox.attribution._process_run_id: str | None = None
```