# `Pixir.SessionLease`
[🔗](https://github.com/Ranvier-Technologies/pixir/blob/main/lib/pixir/session_lease.ex#L1)

Filesystem-backed writer leases for Pixir Sessions.

`Pixir.Session` serializes writes inside one BEAM runtime, but a second OS process can
still try to resume the same Session and append to the same NDJSON Log. A
`SessionLease` is the cross-process live-capability guard: exactly one writer holder
can acquire `<workspace>/.pixir/session_leases/<session_id>.json` with exclusive
create, and `Pixir.Log.append/2` refuses competing appends while a lease is present.

The lease is not History and never replaces the Log. It is local runtime evidence used
to answer "may this process write now?" Snapshot readers do not need a lease. Stale or
ambiguous leases fail closed until an explicit forced-release path records a diagnostic
release entry under `.pixir/session_leases/releases/`.

Lease operations validate the Session id and `lstat` every existing Pixir-owned path
component below the trusted Workspace root. Existing and dangling symlinks fail with
`unsafe_state_path`. This static preflight does not close a same-UID replacement race
between the check and the subsequent file operation.

# `lease`

```elixir
@type lease() :: map()
```

# `acquire`

```elixir
@spec acquire(
  String.t(),
  keyword()
) :: {:ok, lease()} | {:error, map()}
```

Acquire the writer lease for a Session using atomic exclusive file creation.

# `authorize_append`

```elixir
@spec authorize_append(
  String.t(),
  keyword()
) :: :ok | {:error, map()}
```

Authorize a Log append.

Raw appenders are allowed only when no writer lease exists. Active/stale/ambiguous
leases require the matching holder passed as `writer_lease: lease`.

# `force_release`

```elixir
@spec force_release(
  String.t(),
  keyword()
) :: {:ok, map()} | {:error, map()}
```

Explicitly release a stale or ambiguous writer lease.

Active leases are refused. The release operation records a durable diagnostic JSON
entry before removing the lease file. This is a break-glass path for orphaned writer
state, not ordinary startup cleanup.

# `heartbeat`

```elixir
@spec heartbeat(lease()) :: {:ok, lease()} | {:error, map()}
```

Refresh the lease heartbeat for a live Session writer.

# `release`

```elixir
@spec release(lease() | nil) :: :ok | {:error, map()}
```

Release a writer lease if the current holder still owns it.

# `status`

```elixir
@spec status(
  String.t(),
  keyword()
) :: {:ok, map()} | {:error, map()}
```

Return a parseable Session writer lease status snapshot.

---

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