# `X402.Extensions.SIWX.ETSStorage`
[🔗](https://github.com/cardotrejos/x402/blob/v0.9.0/lib/x402/extensions/siwx/ets_storage.ex#L1)

ETS-backed SIWX storage with TTL-based expiration.

Records are stored in an ETS table keyed by `{address, resource}` and cleaned
up periodically by the GenServer process.

Reads are serialized through the GenServer to ensure consistency with
concurrent deletes (e.g., session revocation).

# `server`

```elixir
@type server() :: GenServer.server()
```

Storage server identifier accepted by `GenServer.call/3`.

# `state`

```elixir
@type state() :: %{
  table: atom(),
  cleanup_interval_ms: pos_integer(),
  max_size: pos_integer()
}
```

# `child_spec`
*since 0.3.0* 

```elixir
@spec child_spec(keyword()) :: Supervisor.child_spec()
```

Returns a child specification for `X402.Extensions.SIWX.ETSStorage`.

# `delete`
*since 0.3.0* 

```elixir
@spec delete(String.t(), String.t()) :: :ok
```

Deletes an access record from the default storage server.

# `delete`
*since 0.3.0* 

```elixir
@spec delete(server(), String.t(), String.t()) :: :ok
```

Deletes an access record from a specific storage server.

# `get`
*since 0.3.0* 

```elixir
@spec get(String.t(), String.t()) ::
  {:ok, X402.Extensions.SIWX.Storage.access_record()} | {:error, :not_found}
```

Fetches an access record for a wallet/resource pair from the default server.

Reads go directly to ETS for better concurrency. Expired records return
`{:error, :not_found}`.

# `get`
*since 0.3.0* 

```elixir
@spec get(server(), String.t(), String.t()) ::
  {:ok, X402.Extensions.SIWX.Storage.access_record()} | {:error, :not_found}
```

Fetches an access record from a specific storage server.

Routes through the GenServer to ensure reads are serialised with
concurrent deletes (e.g., session revocation). A direct ETS read on a
`:protected` table sees the table state at read-time, which may be before
a queued-but-not-yet-processed `delete` is applied — meaning a revoked
session can appear valid until the next cleanup sweep.

# `put`
*since 0.3.0* 

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

Stores an access record in the default storage server.

# `put`
*since 0.3.0* 

```elixir
@spec put(server(), String.t(), String.t(), term(), non_neg_integer()) ::
  :ok | {:error, term()}
```

Stores an access record in a specific storage server.

# `start_link`
*since 0.3.0* 

```elixir
@spec start_link(keyword()) ::
  GenServer.on_start() | {:error, NimbleOptions.ValidationError.t()}
```

Starts an ETS-backed SIWX storage process.

## Options

* `:name` (`t:term/0`) - Registered name of the ETS storage process. The default value is `X402.Extensions.SIWX.ETSStorage`.

* `:table` (`t:atom/0`) - Named ETS table used to store SIWX access records. The default value is `:x402_siwx_storage`.

* `:cleanup_interval_ms` (`t:pos_integer/0`) - Interval in milliseconds between cleanup sweeps. The default value is `60000`.

* `:max_size` (`t:pos_integer/0`) - Maximum number of {address, resource} entries to keep in the ETS table. When reached, new `put/4` calls return `{:error, :storage_full}` until the next cleanup sweep evicts expired records. Prevents unbounded memory growth under spam attacks. The default value is `100000`.

---

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