# `WardrikVault`

Keeps credentials sealed in a `WardrikStore`, and gives them to WardrikCore agents' tools.

    children = [
      {WardrikStore, store},
      {WardrikVault, name: MyApp.Vault, store: store, keys: [System.fetch_env!("VAULT_KEY")]}
    ]

    WardrikCore.start_agent(
      agent_id: "digest",
      model: "anthropic:claude-haiku-4-5",
      principal: %WardrikCore.Principal{id: "user:jon"},
      credentials: {WardrikVault, MyApp.Vault}
    )

The vault implements `WardrikCore.Credentials`. A tool gets only what the agent's principal
holds.

Functions that take a vault raise `ArgumentError` if it isn't started.

# `kind`

```elixir
@type kind() :: :secret | :grant
```

A secret someone put in, or a grant someone gave by authorizing.

# `t`

```elixir
@type t() :: atom()
```

A vault, by the name your app gave it.

# `value`

```elixir
@type value() :: String.t() | {:secret, String.t()} | nil
```

An option that holds a secret: the secret itself, or `{:secret, name}` for one of the deployment's.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `delete`

```elixir
@spec delete(t(), WardrikCore.Principal.t(), String.t()) :: :ok | {:error, term()}
```

Removes what `principal` holds as `name`. Returns `:ok`, also when there's nothing to remove.

# `deployment`

```elixir
@spec deployment() :: WardrikCore.Principal.t()
```

Returns the principal that holds the deployment's own secrets.

These are your app's keys, such as a model provider's, and the secrets options name with
`{:secret, name}`. No agent works for this principal.

    {:ok, _vsn} = WardrikVault.put(MyApp.Vault, WardrikVault.deployment(), "SLACK_SIGNING_SECRET", secret)

# `fetch`

```elixir
@spec fetch(t(), WardrikCore.Principal.t(), String.t()) ::
  {:ok, binary()} | {:error, term()}
```

Returns the value `principal` holds as `name`, unsealed.

    WardrikVault.fetch(MyApp.Vault, principal, "github")
    #=> {:ok, "ghp_..."}

Returns `{:error, :not_found}` if there's none, and `{:error, :no_key}` without keys.

# `generate_key`

```elixir
@spec generate_key() :: String.t()
```

Returns a new key, base64-encoded, for `:keys`.

# `list`

```elixir
@spec list(t()) :: [{String.t(), String.t(), kind()}]
```

Returns every credential as `{principal_id, name, kind}`. Needs no key.

    WardrikVault.list(MyApp.Vault)
    #=> [{"user:jon", "github", :secret}]

Names aren't sealed. Anyone who can read the store can see who holds a credential of which name.

# `lookup`

```elixir
@spec lookup(t(), WardrikCore.Principal.t(), String.t()) ::
  {:ok, kind(), binary(), WardrikStore.vsn()}
  | {:error, term()}
  | {:error, kind(), term()}
```

Returns what `principal` holds as `name`, unsealed, with its kind and version.

    WardrikVault.lookup(MyApp.Vault, principal, "github")
    #=> {:ok, :secret, "ghp_...", vsn}

Returns `{:error, kind, reason}` if the value is there but won't unseal.

# `names`

```elixir
@spec names(t(), WardrikCore.Principal.t()) :: [String.t()]
```

Returns the names `principal` holds. Needs no key.

# `put`

```elixir
@spec put(t(), WardrikCore.Principal.t(), String.t(), binary(),
  kind: kind(),
  if_vsn: WardrikStore.vsn()
) :: {:ok, WardrikStore.vsn()} | {:error, term()}
```

Seals `value` as `name` for `principal`. Returns `{:ok, vsn}`, or `{:error, :no_key}` without
keys.

    {:ok, vsn} = WardrikVault.put(MyApp.Vault, principal, "github", "ghp_...")

## Options

  * `:kind` - `:secret` or `:grant`. `:secret` unless set.
  * `:if_vsn` - writes only over this version. See `WardrikStore.put/4`.

# `read`

```elixir
@spec read(WardrikStore.t(), [String.t()], WardrikCore.Principal.t(), String.t()) ::
  {:ok, binary()} | {:error, term()}
```

Returns the value `principal` holds as `name`, without a running vault.

Use it to read a secret before the vault starts, such as while reading config. `store` and
`keys` are what the vault is started with.

    WardrikVault.read(store, [System.fetch_env!("VAULT_KEY")], principal, "github")
    #=> {:ok, "ghp_..."}

# `read_value`

```elixir
@spec read_value(WardrikStore.t(), [String.t()], value()) ::
  {:ok, String.t() | nil} | {:error, term()}
```

Returns the secret an option holds, as `value/2` does, without a running vault. See `read/4`.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts the vault.

    WardrikVault.start_link(
      name: MyApp.Vault,
      store: store,
      keys: [System.fetch_env!("VAULT_KEY"), System.fetch_env!("OLD_VAULT_KEY")]
    )

Values are sealed with AES-256-GCM. The first key seals. Any key unseals. To rotate, put a new
key first and keep the old one until nothing needs it.

## Options

  * `:name` - required. What the vault is reached by.
  * `:store` - required. The `WardrikStore` to keep credentials in.
  * `:keys` - base64 keys from `generate_key/0`. `[]` unless set. Without keys, nothing is
    sealed or unsealed. A key that isn't 32 bytes is dropped with a warning.
  * `:grants` - `{module, opts}`, where `module` implements `WardrikVault.Grants`. `nil` unless
    set.

# `value`

```elixir
@spec value(t(), value()) :: {:ok, String.t() | nil} | {:error, term()}
```

Returns the secret an option holds.

    WardrikVault.value(MyApp.Vault, {:secret, "SLACK_SIGNING_SECRET"})
    #=> {:ok, "8f14..."}

    WardrikVault.value(MyApp.Vault, "8f14...")
    #=> {:ok, "8f14..."}

`{:secret, name}` reads the deployment's secret on each call, so a new one takes effect at once.
A string or `nil` is returned as it is.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
