# `Replicant.Config`
[🔗](https://github.com/baselabs/replicant/blob/v0.3.1/lib/replicant/config.ex#L1)

Validates `Replicant.start_link/1` options and enforces the **go-forward-only
start guard**: a `:state_mirror` sink resuming from an empty
checkpoint without `go_forward_only: true` would silently deliver partial data
from the slot's creation point, so it is refused at start.

Pure — no processes, no connection. `Replicant.start_link/1` (the facade) calls
`validate/1` then `guard/1` before spawning a pipeline.

# `t`

```elixir
@type t() :: %{
  optional(:batch) =&gt; keyword() | nil,
  optional(:batch_delivery) =&gt; keyword() | nil,
  optional(:streaming) =&gt; keyword() | nil,
  connection: keyword(),
  slot_name: String.t(),
  publication: [String.t()],
  sink: module(),
  go_forward_only: boolean(),
  snapshot: boolean() | keyword(),
  max_inflight_lag: pos_integer(),
  max_command_retries: non_neg_integer(),
  checkpoint_store: keyword() | nil,
  failover: boolean(),
  messages: boolean()
}
```

# `guard`

```elixir
@spec guard(t()) :: :ok | {:error, :go_forward_required}
```

The go-forward-only start guard. Refuses ONLY the exact unsafe triple — a
`:state_mirror` sink (default kind) with a **definitively empty** checkpoint
(`{:ok, nil}`) and `go_forward_only: false`. An `:append_log` sink, a non-nil
checkpoint, `go_forward_only: true`, OR a checkpoint READ fault (raise/exit/
`{:error, _}`) all pass — the read-fault path is deliberately fail-open (a
re-dispatched already-persisted txn is deduped by the idempotent sink; only a
definitive empty checkpoint proves partial-delivery risk).
`snapshot: true` ALSO bypasses the empty-checkpoint refusal (alongside
`go_forward_only: true`): the backfill IS the safe seed, so an empty checkpoint
is the expected first-run state, not a partial-delivery risk.

# `validate`

```elixir
@spec validate(keyword()) ::
  {:ok, t()}
  | {:error,
     :config_invalid
     | :invalid_identifier
     | :invalid_sink
     | :conflicting_start_mode
     | :snapshot_unsupported
     | :batch_unsupported
     | :messages_unsupported}
```

Validate raw `start_link` options into a normalised config map, or return a
plain-atom error: `:config_invalid` (missing/mis-shaped connection or opts),
`:invalid_identifier` (slot/publication fails the Postgres-identifier
allowlist), `:invalid_sink` (sink is not a module exporting the two mandatory
callbacks), `:conflicting_start_mode` (`go_forward_only: true` AND any snapshot
intent — `snapshot: true` OR `snapshot: [mode: :incremental]` — mutually
exclusive start intents), `:snapshot_unsupported` (a snapshot intent whose sink
is missing the required callbacks for that mode: `snapshot: true` needs both v1
callbacks; sink-owned `snapshot: [mode: :incremental]` needs `handle_snapshot/2`
+ `snapshot_progress/0`), `:batch_unsupported` (`batch_delivery` is set but the
sink does not implement `handle_batch/1`), `:messages_unsupported` (`messages: true`
is set but the sink does not implement `handle_message/2`). A `snapshot` value
that is neither a boolean nor a `[mode: :incremental, ...]` keyword (or has
non-positive knobs) is `:config_invalid`.

---

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