Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,11 @@ Env-only is enough if `SIP_SERVER` and `SIP_USERNAME` are set. Never commit real

Transfer / dial targets: bare extension (`102`), `user@host`, full `sip:` URI, `tel:` URI, or a PSTN number (`+1…`, punctuation OK). Bare extensions become `sip:{ext}@{SIP_SERVER}`. Phone numbers are reduced to digits (leading `+` stripped) so typical PBX outbound routes match. A `tel:` URI is always that number on `SIP_SERVER`: `;phone-context`, `;ext`, and a trailing `@host` are not a SIP target. Dial failure JSONL is `dial failed: <uri> (<SIP status>)` when the far end sent one (e.g. `603 Decline`).

Flags: `--auto-answer`, `--play FILE` (with auto-answer), `--settings PATH`, `--config N`, `--pcmu-only`, `--no-keepalive`. `sipbot serve --help` states the play/resample rule.
Flags: `--auto-answer`, `--play FILE` (with auto-answer), `--settings PATH`, `--config N`, `--pcmu-only`, `--no-keepalive`, `--extended-retry`, `--sip-trace`. `sipbot serve --help` states the play/resample rule.

**Registration:** `status.registered` is `false` while registration is failing (it used to stay `true` through an outage). After a temporary failure (timeout, 5xx) sipbot retries: by default up to 5 attempts at 2 s × n, then SIPSorcery's own retry every 300 s. `--extended-retry` (or `SIP_EXTENDED_RETRY=1`) retries forever instead, 30 s doubling to at most 5 min; use it for always-on hosts. A hard failure (wrong password, 402, 403, 404) is never retried: restart sipbot after fixing the account. The first stderr line reports `extendedRetry=` and `sipTrace=`.

**SIP trace:** `--sip-trace` (or `SIP_TRACE=1`) logs every SIP message sent and received to stderr. Lab use only: the messages include headers such as digest `Authorization` responses. Off by default. Both switches are read by sipbot only, not by the library's settings loader.

## Minimal inbound flow

Expand Down
61 changes: 61 additions & 0 deletions Config/SipBotSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,65 @@ public class SipConfig

[JsonPropertyName("fromname")]
public string FromName { get; set; } = string.Empty;

/// <summary>
/// How <see cref="SipClient"/> retries registration after a failure. The defaults keep the
/// library's original schedule.
/// </summary>
[JsonPropertyName("registrationRetry")]
public RegistrationRetryOptions RegistrationRetry { get; set; } = new();
}

/// <summary>
/// Registration retry settings for <see cref="SipClient"/>.
/// </summary>
/// <remarks>
/// A temporary failure (timeout, 5xx, and any status that is not a hard failure) is retried.
/// A hard failure (401/407 after authentication, 402, 403, 404) is never retried automatically:
/// the client stops registering until <see cref="SipClient.StartRegistration"/> is called again.
/// </remarks>
public class RegistrationRetryOptions
{
/// <summary>
/// Extended retry, for always-on hosts: after a temporary failure the client retries forever,
/// the delay doubling from <see cref="ExtendedInitialDelayMs"/> up to
/// <see cref="ExtendedMaxDelayMs"/>. Off by default.
/// </summary>
[JsonPropertyName("extended")]
public bool Extended { get; set; }

[JsonPropertyName("extendedInitialDelayMs")]
public int ExtendedInitialDelayMs { get; set; } = 30_000;

[JsonPropertyName("extendedMaxDelayMs")]
public int ExtendedMaxDelayMs { get; set; } = 300_000;

/// <summary>
/// Default retry: after a temporary failure the client makes up to this many attempts,
/// attempt n after <see cref="DefaultBaseDelayMs"/> × n. After the last one the registration
/// agent's own retry stays armed, so the process still recovers without a restart.
/// </summary>
[JsonPropertyName("defaultMaxAttempts")]
public int DefaultMaxAttempts { get; set; } = 5;

[JsonPropertyName("defaultBaseDelayMs")]
public int DefaultBaseDelayMs { get; set; } = 2_000;

// Timings below are passed through to SIPSorcery or drive the health check. They are not
// part of the settings file; tests shorten them.

/// <summary>Seconds between the registration agent's own retries after a failure (SIPSorcery default 300).</summary>
[JsonIgnore]
internal int AgentFailureRetrySeconds { get; set; } = 300;

/// <summary>Seconds the registration agent waits for a REGISTER response (SIPSorcery default 60).</summary>
[JsonIgnore]
internal int AgentAttemptTimeoutSeconds { get; set; } = 60;

[JsonIgnore]
internal int HealthCheckIntervalMs { get; set; } = 30_000;

/// <summary>How long registration must be missing before the health check acts.</summary>
[JsonIgnore]
internal int HealthCheckStaleMs { get; set; } = 120_000;
}
Loading
Loading