Skip to content

docs: gateway is now safe-by-default (bind-aware admission + drain) — update reliability pages for PraisonAI PR #3442 #2500

Description

@MervinPraison

Source of change

Upstream PR MervinPraison/PraisonAI#3442"fix: make gateway reliability safe by default" (merged 2026-07-29T10:41:22Z, fixes MervinPraison/PraisonAI#3438).

Cross-referenced from the "Out of scope" section of docs issue #2487 — the sibling PR-#3437 issue explicitly says #3442 needs its own docs ticket. This is that ticket.


Update vs. new content — decision

UPDATE two existing pages. No new page.

The reliability= preset already has dedicated documentation. What changed is what happens when the operator leaves the knob unset (None) — the same code path, one different default. The right shape for the docs is to update the existing pages so the "unset" row/example is no longer a footgun, not to spin up a new page that competes with them.

Concretely:

# Action File Why
1 UPDATE docs/features/gateway-reliability.mdx The page describes the profile table today; the "unset" / "default" row is now stale and misleading (it still implies no admission ceiling).
2 UPDATE docs/features/gateway-reliability-preset.mdx The primary "how to enable a reliability preset" page currently frames reliability="production" as the only safe posture. That framing is now wrong — the unset posture is safe too. Rewrite the intro + the profile table + best-practices.
3 UPDATE (light) docs/features/gateway-cli.mdx (only if the --reliability flag is mentioned there) Add the new reliability="off" semantics and the note that --reliability off is the explicit opt-out now that unset is safe.
4 AUTO docs/sdk/reference/** Re-run the parity script after merge — _reliability.py docstrings changed. Do NOT hand-edit auto-generated files (AGENTS.md §1.7).

Placement rules (AGENTS.md §1.8):

  • Do NOT touch docs/concepts/ — human-only.
  • Do NOT touch docs/js/ or docs/rust/ — auto-managed; the reliability primitive is Python-gateway only.
  • All edits are in docs/features/ — existing human-written pages being extended in place.

No new page → no docs.json change.


What the change actually is (user-facing summary)

Before PR #3442, running the gateway the obvious way — BotOS(agent=...), praisonai gateway start, or a minimal gateway.yaml — silently gave you a no-backpressure deployment: no admission ceiling and only a 5-second drain, so a burst could fan out unboundedly and a rolling restart could cut in-flight turns mid-response. The production preset already existed; the problem was the default.

After PR #3442, the unset posture (reliability=None) is safe by default and is bind-aware:

  • Unset + loopback bind (localhost, 127.0.0.1, ::1, 127.0.0.2, [::1], or None) → bounded admission ceiling + fair wait queue + strict outbound ordering, with a snappy 5s drain.
  • Unset + non-loopback bind (0.0.0.0, ::, any real interface, any DNS name) → the full production window (15s drain), same admission + strict ordering.
  • reliability="off" → explicit opt-out to today's immediate-teardown / no-backpressure behaviour.
  • reliability="default" → the explicit legacy posture (5s drain, no ceiling, best-effort ordering) for callers who ask for it. This is now distinct from None — before PR #3442 they were aliases; they're not any more.
  • Explicit drain_timeout / max_concurrent_runs / admission_policy / outbound_ordering still always win — nothing about the "explicit overrides beat the preset" rule changes.

No new public knobs. No CLI/YAML/Python surface addition. Just a different default value that the same three surfaces now resolve to when nothing is passed.


SDK ground truth — read these files before writing

Per AGENTS.md §1.2 (SDK-first cycle), verify every parameter, threshold, and default against the source before drafting any prose:

Path (repo-root layout) What lives here
praisonai_bot/bots/_reliability.py resolve_reliability() — the single point where the unset default is decided. Read the module docstring (rewritten in this PR) end-to-end — it is the canonical description of the four postures.
praisonai_bot/bots/_reliability.py::_is_externally_bound() The bind-classification helper. Loopback is detected numerically via ipaddress.ip_address(...).is_loopback, so every valid loopback form is recognised, not just 127.0.0.1/::1. None bind → loopback (never guessed external without evidence). A bare hostname that isn't a literal IP is treated as external.
praisonai_bot/bots/_reliability.py::_LOOPBACK_HOSTNAMES The hostname allowlist ("", "localhost", "loopback") that avoids DNS lookups.
praisonai_bot/cli/features/gateway.py::_commit_start_flags The CLI no-config wiring — reliability is always resolved now (previously only when max_concurrent_runs or reliability was passed) and the bind host is threaded through so an external bind gets the full production window.
praisonai_bot/tests/unit/bots/test_reliability.py 8 new/renamed tests — the best copy-paste source for the exact behavior contract (unset loopback, unset external, non-canonical loopback, default vs off, BotOS wiring, CLI no-config wiring).
praisonai_bot/tests/unit/bots/test_outbox_ordering.py Confirms unset posture now upgrades outbound ordering to strict (previously best-effort), while explicit default / off stay best_effort for backward compat.

Do not document behavior absent from these files (AGENTS.md §1.3). In particular:

  • _DEFAULT_DRAIN_SECONDS = 5.0 and _PRODUCTION_DRAIN_SECONDS = 15.0 are the two drain windows that appear in the resolved posture. Do NOT invent other numbers or paraphrase them.
  • _cpu_scaled_ceiling() produces the admission ceiling. It is conservative and CPU-scaled — describe it as such; do NOT quote a fixed number, since the actual value depends on the host.
  • overflow_policy defaults to "queue" in the safe posture (bounded fair queue, not "reject"). Verify against resolve_reliability for the exact string.
  • The unset posture now returns outbound_ordering="strict" (verified in test_reliability_unset_default_is_strict). Explicit "default"/"off" keep best_effort.

Page-by-page spec

1. UPDATE docs/features/gateway-reliability.mdx

The current page lists a "Profiles" or "Presets" table with production / default / off rows. Rewrite so four distinct postures are shown, in this order:

Preset Drain Admission ceiling Wait queue Outbound ordering When to use
Unset (reliability=None, loopback bind) 5s Yes (CPU-scaled) Yes (bounded, fair) strict Local dev on 127.0.0.1 / localhost — safe by default
Unset (reliability=None, non-loopback bind) 15s Yes (CPU-scaled) Yes (bounded, fair) strict An actual deployment (0.0.0.0, real hostname) — full production posture is auto-picked
"production" (explicit) 15s Yes (CPU-scaled) Yes (bounded, fair) strict Explicitly ask for the production window regardless of bind
"default" (explicit) 5s No No best-effort Legacy behavior — you know what you're doing and want the pre-#3438 shape
"off" 0s No No best-effort Explicit opt-out — immediate teardown, no backpressure. This is now the only way to get today's pre-safe behavior — leaving the knob unset no longer does that.

Rewrite the surrounding prose so:

  • The "sensible defaults" claim reflects that the unset posture is safe (not just "production").
  • The "default" preset section makes it clear this is now distinct from None — before PR #3442 they were aliases; they're not any more. Say so explicitly so a reader upgrading a running deployment isn't surprised.
  • Add a <Warning> block explaining that operators who deliberately wanted no-admission / no-drain behavior must now pass reliability="off" (or reliability="default" for the 5s-drain-no-ceiling shape) explicitly. Cite PraisonAI #3438 by number.

Explicit overrides still win — the existing wording about drain_timeout=30.0 beating the preset's 15s stays correct; no change needed there.

2. UPDATE docs/features/gateway-reliability-preset.mdx

This is the primary landing page. Two structural changes:

(a) Intro rewrite. The current text is:

The user deploys with a reliability preset; retries, timeouts, and drain behaviour apply automatically.

Replace with:

A BotOS(agent=..., ...) or praisonai gateway start with no reliability= argument is now safe by default — the gateway picks a bounded admission ceiling, a fair wait queue, strict outbound ordering, and a drain window sized to the bind (loopback → 5s, real interface → 15s). The reliability= preset is still there for callers who want to override that.

(b) Quick Start rewrite. The current single example is:

bot = BotOS(agent=agent, platforms=["telegram", "discord"], reliability="production")
bot.start()

Rewrite as <Steps> showing progressive disclosure:

Step 1 — Simplest, safe by default:

from praisonaiagents import Agent
from praisonai_bot.bots.botos import BotOS

agent = Agent(name="assistant", instructions="Help the user.")

# Safe by default: admission ceiling + fair queue + drain, bind-aware.
bot = BotOS(agent=agent, platforms=["telegram", "discord"])
bot.start()

Step 2 — Force the full production window even on loopback:

bot = BotOS(
    agent=agent,
    platforms=["telegram", "discord"],
    reliability="production",  # 15s drain regardless of bind
)

Step 3 — Explicit opt-out (revert to pre-#3438 immediate teardown):

bot = BotOS(
    agent=agent,
    platforms=["telegram", "discord"],
    reliability="off",  # no drain, no admission — you asked for it
)

Then the "Best Practices" / accordion section:

  • Add an accordion"I want the pre-3438 behavior back" — pointing at reliability="off" (no drain) or reliability="default" (5s drain, no admission). Do NOT recommend either as a default.
  • Update the existing "Recommended: production posture" accordion to explicitly say the unset posture is now equivalent to production on a non-loopback bind, so most operators don't need to pass anything.

Add or extend the "How resolution works" diagram — the decision tree needs to show the bind-classification branch. Use the AGENTS.md palette exactly (#8B0000 inputs, #189AB4 process, #10B981 result, #F59E0B warnings, #6366F1 config; white text; classDef):

graph TB
    R{reliability=} -->|None| B{bind_host}
    R -->|"production"| P[15s drain + admission + fair queue + strict]
    R -->|"default"| D[5s drain, no admission, best-effort]
    R -->|"off"| O[0s drain, no admission, best-effort]
    B -->|loopback / localhost / 127.x / ::1| SL[5s drain + admission + fair queue + strict]
    B -->|0.0.0.0 / real interface / hostname| SE[15s drain + admission + fair queue + strict]

    classDef q fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff
    classDef optout fill:#8B0000,stroke:#7C90A0,color:#fff

    class R,B q
    class SL,SE,P result
    class D input
    class O optout
Loading

3. UPDATE (light) docs/features/gateway-cli.mdx

Search the page for --reliability. If it is documented:

  • Add a note in the --reliability row: "Unset (omitting the flag) is safe by default as of PR #3442 — the CLI resolves to a bounded admission ceiling + fair queue with a bind-aware drain (5s on loopback, 15s on external binds). Pass --reliability off to get the pre-3438 immediate-teardown behavior back."
  • If the page has an example that shows --reliability production as the "recommended production setup", downgrade it: mention that with a non-loopback bind, no --reliability flag is needed at all.

If --reliability is not already documented on gateway-cli.mdx, add nothing there — the reliability-specific pages carry the CLI examples too.


User-facing interaction flow (must be shown on the reliability page)

Show the "surprise-me" scenario, since that's what the PR is preventing:

sequenceDiagram
    participant Op as Operator
    participant Gw as Gateway
    participant Ag as In-flight turn

    Note over Op: Before #3442
    Op->>Gw: praisonai gateway start (no reliability flag)
    Gw-->>Op: (no admission, 5s drain)
    Op->>Gw: rolling restart
    Gw-->>Ag: SIGTERM — turn cut mid-response ❌

    Note over Op: After #3442 (safe by default)
    Op->>Gw: praisonai gateway start --host 0.0.0.0
    Gw-->>Op: (admission ceiling + fair queue + 15s drain)
    Op->>Gw: rolling restart
    Gw-->>Ag: SIGTERM — drain window; turn completes ✅
Loading

Migration note the docs MUST call out

Because reliability="default" and reliability=None used to be aliases and are now different:

  • Any existing user code that literally passes reliability="default" will keep exactly the same behavior it has today (5s drain, no admission).
  • Any existing user code that passes nothingBotOS(agent=agent) or praisonai gateway start with no --reliability — will silently upgrade to the safe posture (admission ceiling, fair queue, strict outbound ordering, bind-aware drain).

For most deployments this is strictly an improvement, but two behaviors change observably:

  1. Backpressure appears. A burst that used to fan out unboundedly will now queue (or reject, when the queue is full). Downstream code that assumed "the gateway never says no" needs to handle admission rejection.
  2. Restart takes 5–15s. A SIGTERM-then-kill loop that used to be near-instant will now wait for in-flight turns to complete (or be cut at the drain window). Deployment scripts that expected sub-second restart need to allow the drain window.

Both are called out under a <Warning> on gateway-reliability-preset.mdx with the escape hatch — pass reliability="off" (or --reliability off) to revert.


Verification checklist for the follow-up docs agent

Before opening the PR:

  • Read praisonai_bot/bots/_reliability.py end to end — the rewritten module docstring is the canonical source for the four postures and their behaviors. Quote its numbers exactly.
  • Verify _LOOPBACK_HOSTNAMES = frozenset({"", "localhost", "loopback"}) — do NOT add or remove entries.
  • Verify _is_externally_bound(None) is False (unknown → loopback), _is_externally_bound("127.0.0.2") is False (numeric loopback range), _is_externally_bound("example.com") is True (bare hostname → external). These are the exact edge cases the tests pin.
  • Verify the two constants: _DEFAULT_DRAIN_SECONDS = 5.0, _PRODUCTION_DRAIN_SECONDS = 15.0. Use those verbatim.
  • Verify resolve_reliability(None).outbound_ordering == "strict" and resolve_reliability("default").outbound_ordering == "best_effort" — the ordering change is easy to miss.
  • Verify resolve_reliability(None).overflow_policy — read the code, do not guess. ("queue" when the safe posture is picked; "reject" only when the operator passed it.)
  • Read praisonai_bot/cli/features/gateway.py::_commit_start_flags — confirm the resolver is always called now (not only when a flag is passed) and that bind_host=host is threaded through.
  • Cross-check tests/unit/bots/test_reliability.py — 8 tests capture the contract, including the CLI no-config paths. Use them as example sources.
  • Every code snippet in the updated pages runs copy-paste (imports present, no placeholders).
  • All Mermaid diagrams use the standard palette (#8B0000 / #189AB4 / #10B981 / #F59E0B / #6366F1) with classDef + white text.
  • No forbidden phrases (AGENTS.md §6.3): "In this section", "As you can see", "Please note", etc.
  • File placements: only docs/features/gateway-reliability.mdx, docs/features/gateway-reliability-preset.mdx, and optionally docs/features/gateway-cli.mdx. Zero edits to docs/concepts/, docs/js/, docs/rust/, docs/sdk/reference/**, or docs.json.
  • Branch created (not committed to main) and PR opened as draft.

Writing style (AGENTS.md §6)

  • One-sentence intro to each new section. Lead with the outcome — "You now get backpressure by default" — not with the resolver internals.
  • Non-developer friendly (AGENTS.md §6.1): the whole update should feel like "is it really this easy — I get the safe posture just by not passing anything?"
  • Agent-centric first (AGENTS.md §1.1 rule 9): the Quick Start on gateway-reliability-preset.mdx should open with BotOS(agent=agent) (the safe default), not with reliability="production".
  • Friendly imports only: from praisonai_bot.bots.botos import BotOS and from praisonaiagents import Agent — verify against praisonai_bot/bots/__init__.py first.
  • The two <Warning> blocks are the important stylistic addition — they exist so a running deployment being upgraded is not surprised.

References


Filed by the docs-triage routine after the PR merge webhook fired. The follow-up docs agent should treat this as the acceptance checklist for the "gateway safe-by-default reliability" documentation cycle.

Metadata

Metadata

Assignees

No one assigned

    Labels

    claudeTrigger Claude Code analysisdocumentationImprovements or additions to documentationgateway

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions