## `eigenkit.core.secret_fields`

Secret configuration fields, which are kept in memory and never saved.

A configuration dataclass marks a field that holds a credential with `metadata={"secret": True}`, and with `repr=False`, so that the field stays out of `repr()`, `str()` and the `metadata.json` of a saved model. Two things respect the mark:

- `SecretFieldsMixin` makes pickling drop those fields. joblib checkpoints and process pools also use pickle, so they drop them as well. Copies inside one process (`copy.copy`, `copy.deepcopy`, and therefore `sklearn.base.clone`) keep them, so a grid search can still authenticate.
- The `.eigenkit` model serialiser writes a configuration with `dataclasses.asdict()`, which ignores both `repr=False` and the pickle hooks, so it explicitly drops every name in `secret_field_names()`.

After a load, a secret field holds its default value (usually `None`), and the backend falls back to its own way of finding credentials, usually an environment variable.

Example

```python
import pickle
from dataclasses import dataclass, field

from eigenkit.core.secret_fields import (
    SecretFieldsMixin,
    secret_field_names,
)


@dataclass(frozen=True)
class MyConfig(SecretFieldsMixin):
    device: str = "sim"
    token: str | None = field(default=None, repr=False, metadata={"secret": True})


cfg = MyConfig(token="s3cr3t")
print(secret_field_names(cfg))            # frozenset({'token'})
print(cfg)                                # MyConfig(device='sim')
print(pickle.loads(pickle.dumps(cfg)).token)  # None
```

### `SecretFieldsMixin`

A mixin for frozen configuration dataclasses that hold credentials.

Pickling (and therefore joblib) drops every secret field, and unpickling gives each of them its default value. Copying (`copy.copy`, `copy.deepcopy`, `sklearn.base.clone`) keeps every field, including the secret ones.

Mark a field secret with `field(default=None, repr=False, metadata={"secret": True})`, and put the mixin first among the bases of a `@dataclass(frozen=True)` class.

#### `__copy__()`

Return a shallow copy that keeps the secret fields.

#### `__deepcopy__(memo)`

Return a deep copy that keeps the secret fields.

#### `__getstate__()`

Return every field that is not secret, for pickle.

#### `__setstate__(state)`

Restore the pickled fields and set every secret field to its default.

### `secret_field_names(config)`

Return the names of the dataclass fields that are marked secret.

A field is secret when the `"secret"` entry of its metadata is true.

Parameters:

| Name     | Type  | Description                                            | Default    |
| -------- | ----- | ------------------------------------------------------ | ---------- |
| `config` | `Any` | The dataclass instance, or dataclass type, to inspect. | *required* |

Returns:

| Type             | Description                                                                   |
| ---------------- | ----------------------------------------------------------------------------- |
| `frozenset[str]` | The names of the secret fields. The set is empty when the dataclass has none. |

Raises:

| Type        | Description                   |
| ----------- | ----------------------------- |
| `TypeError` | If config is not a dataclass. |
