Gateway CLI

Command-line interface for managing the PraisonAI Gateway daemon and server

python
from praisonaiagents import Agent

agent = Agent(name="gateway-cli-agent", instructions="Manage gateway via CLI commands.")
agent.start("Start the gateway and show its status.")

Gateway CLI provides commands for starting, monitoring, and managing the PraisonAI Gateway server and its daemon service, including channel supervision controls for resilient bot management.

yaml
# gateway.yaml
agents:
  assistant:
    instructions: "You are a helpful AI assistant."
    model: gpt-4o-mini

channels:
  telegram:
    token: "${TELEGRAM_BOT_TOKEN}"
    platform: telegram
bash
# Onboarded: no flag needed — the CLI discovers ~/.praisonai/bot.yaml
praisonai gateway start

# Explicit path still wins
praisonai gateway start --config gateway.yaml

The user runs praisonai gateway start; the CLI launches the daemon, supervises channels, and keeps the WebSocket gateway reachable.

Diagram

Gateway CLI Flow

👤 User

💻 CLI

⚙️ Daemon

🗼 Gateway

🏥 Health Check

How It Works

Diagram
Gateway CLIAgentUserGateway CLIAgentUserRequestProcess requestResult Agent–>>User: Response

Quick Start

  1. Start Gateway
    bash
    # Canonical (bot-tier, no wrapper required)
    praisonai-bot gateway start
    
    # Wrapper alias (requires pip install praisonai)
    praisonai gateway start

    With no --config, start auto-discovers the onboarded config and prints the file it picked before binding:

    bash
    $ praisonai gateway start
    Using gateway config: /Users/you/.praisonai/bot.yaml
  2. Check Status
    bash
    praisonai gateway status
  3. Test Health Endpoint
    bash
    curl http://127.0.0.1:8765/health

Commands

Which verb do I need?

Diagram

Bring it up

Is it healthy?

It’s degraded

A channel misbehaves

Change wiring

Bring it down cleanly

What do I need?

start

status –deep

doctor / doctor –fix

pause / resume / reconnect

hooks add / list / remove

stop –drain-timeout

Gateway Management

CommandDescriptionExample
praisonai gateway startStart the gateway server (foreground)praisonai gateway start --port 9000
praisonai gateway stopStop a running gateway instancepraisonai gateway stop --force
praisonai gateway statusCheck gateway and daemon statuspraisonai gateway status --daemon-only
praisonai gateway doctorValidate channel credentials and route/binding targets before start, run plugin health checks, and optionally auto-repair weak auth tokens (--fix)praisonai gateway doctor --config gateway.yaml --fix
praisonai gateway testOne-shot readiness (probes + shell wiring + optional turn)praisonai gateway test --config gateway.yaml
praisonai gateway channelsList configured channels (add --probe to check creds)praisonai gateway channels --probe
praisonai gateway channels {add,update,disable,remove}Edit one channel in place (validated before writing)praisonai gateway channels add discord --set group_policy=mention_only

Daemon Service

CommandDescriptionExample
praisonai gateway installInstall as OS daemonpraisonai gateway install --no-start
praisonai gateway uninstallRemove daemon servicepraisonai gateway uninstall
praisonai gateway logsShow daemon logspraisonai gateway logs -n 100
praisonai gateway restartGraceful drain + relaunch (daemon-aware)praisonai gateway restart --drain-timeout 30

The installed unit stops (not restarts) on the gateway’s fatal-config exit code (78, EX_CONFIG). See Gateway Exit Codes for the full contract.

Channel Control

CommandDescriptionExample
praisonai gateway pausePause a channelpraisonai gateway pause telegram
praisonai gateway resumeResume a paused channelpraisonai gateway resume telegram
praisonai gateway reconnectForce reconnect a channelpraisonai gateway reconnect telegram

Inbound Hooks

CommandDescriptionExample
praisonai gateway hooks add <path>Register an inbound webhook triggerpraisonai gateway hooks add gmail --agent assistant
praisonai gateway hooks listList registered hookspraisonai gateway hooks list
praisonai gateway hooks remove <path>Remove an inbound hookpraisonai gateway hooks remove gmail

See Gateway Inbound Hooks for full details.

Testing & Debugging

CommandDescriptionExample
praisonai gateway testFull readiness check (see Gateway Readiness)praisonai gateway test --config gateway.yaml --channel slack --turn "Say OK"
praisonai gateway sendSend test messagepraisonai gateway send --channel telegram --channel-id 12345 -m "test"
praisonai gateway diagnostics exportWrite a pre-sanitised support bundle (see Diagnostics Export)praisonai gateway diagnostics export --config gateway.yaml

Diagnostics

praisonai gateway diagnostics export writes a portable, pre-sanitised .zip you can attach to a bug report. Full details in Diagnostics Export.

FlagTypeDefaultDescription
--config, -cstr"gateway.yaml"Path to gateway.yaml.
--output-dir`str \None`~/.praisonai/diagnosticsDirectory to write the bundle.
--log-linesint200Number of recent (redacted) log lines to include.
--jsonboolFalseEmit JSON ({path, manifest}) instead of the human summary.

Command Reference

start

bash
praisonai gateway start [OPTIONS]

Options:
  --host TEXT                     Host to bind to [default: 127.0.0.1]
  --port INTEGER                  Port to listen on [default: 8765 or $GATEWAY_PORT]
  --agents TEXT                   Path to agent configuration file
  --config TEXT                   Path to gateway.yaml for multi-bot mode.
                                  When omitted, auto-discovers the onboarded
                                  config (./bot.yaml → ~/.praisonai/bot.yaml →
                                  ./gateway.yaml alias) and prints
                                  "Using gateway config: <path>". Exits 78 with
                                  an onboard hint if none is found.
  --reliability [production|default|off]
                                  Reliability preset — composes drain + admission.
                                  Omitting the flag is safe by default (#3442): the CLI
                                  resolves a bounded admission ceiling + fair queue with a
                                  bind-aware drain (5s on loopback, 15s on external binds).
                                  production (15s drain regardless of bind), default (5s
                                  drain, no admission), off (pre-3438 immediate teardown).
                                  Overrides `reliability:` from YAML but is overridden
                                  by explicit --drain-timeout / --max-concurrent-runs.
  --preflight / --no-preflight    Validate channel credentials before starting.
                                  A channel with a bad token is reported and
                                  skipped (parked degraded, auto-recovers); the
                                  gateway still serves every healthy channel.
                                  Use --no-preflight to skip probing entirely,
                                  or --strict-preflight to abort on any bad token
                                  [default: --preflight]
  --strict-preflight / --no-strict-preflight
                                  Fail fast and abort the whole gateway if ANY
                                  channel credential is bad, instead of isolating
                                  that one channel as degraded. Defaults to the
                                  config's gateway.preflight.strict (off) (#4862)
  --strict-tools / --no-strict-tools
                                  Fail fast if any tool named in the config
                                  cannot be resolved. Use --no-strict-tools to
                                  skip unresolved tools and start anyway (#3553)
                                  [default: --strict-tools]
  --verify-turn / --no-verify-turn
                                  Run one real agent turn before serving so a
                                  missing/invalid model credential fails at
                                  startup, not on the first user message.
                                  Default: on (reads
                                  gateway.preflight.verify_turn, defaults to
                                  true). Use --no-verify-turn for constrained/
                                  offline environments (#4042).
  --identity-store TEXT           Path to the cross-platform identity link-map
                                  JSON (default ~/.praisonai/identity.json).
                                  Enables one continuous session + memory per
                                  paired/linked user across channels (#3020).
                                  Overrides the `identity:` block in gateway.yaml.
  --scale-to-zero                 Quiesce the gateway when idle for --idle-minutes
                                  (scale-to-zero; #3021). Overrides
                                  lifecycle.scale_to_zero.enabled in gateway.yaml.
  --idle-minutes FLOAT            Minutes of no inbound / in-flight work before
                                  quiescing (#3021). Overrides
                                  lifecycle.scale_to_zero.idle_minutes.
  --drain-marker TEXT             Path to watch for an epoch-aware external drain
                                  marker file (#3021). Overrides
                                  lifecycle.drain.marker_path.
  --trusted-proxy TEXT            CIDR/IP of an upstream proxy or tunnel to trust
                                  for real-client-IP resolution (repeatable,
                                  e.g. --trusted-proxy 10.0.0.0/8). Behind a
                                  reverse proxy this keeps per-IP rate-limits and
                                  the operator id keyed on the real client;
                                  unattributable proxies fail closed (#5312).
                                  Overrides gateway.trusted_proxies in YAML.

Examples:
  praisonai gateway start                        # onboarded: discovers ~/.praisonai/bot.yaml
  praisonai gateway start --config gateway.yaml  # explicit path still wins
  praisonai gateway start --agents agents.yaml --port 9000
  praisonai gateway start --config gateway.yaml --no-preflight
  praisonai gateway start --config gateway.yaml --strict-preflight     # opt into fail-closed
  praisonai gateway start --config gateway.yaml --no-strict-preflight  # override YAML strict:true
  praisonai gateway start --config gateway.yaml --no-strict-tools
  praisonai gateway start --config gateway.yaml --no-verify-turn
  praisonai gateway start --config gateway.yaml --reliability production
  GATEWAY_PORT=9000 praisonai gateway start

  # Enable cross-platform continuity without touching gateway.yaml
  praisonai gateway start --config gateway.yaml \
    --identity-store ~/.praisonai/identity.json

  # Enable scale-to-zero + external drain marker without touching gateway.yaml
  praisonai gateway start --config gateway.yaml \
    --scale-to-zero --idle-minutes 10 \
    --drain-marker /data/gateway.drain

  # Trust upstream proxies for real-client-IP resolution (repeatable, additive)
  praisonai gateway start --config gateway.yaml \
    --trusted-proxy 10.0.0.0/8 \
    --trusted-proxy 192.168.1.10

stop

bash
praisonai gateway stop [OPTIONS]

Options:
  --host TEXT         Gateway host [default: 127.0.0.1]
  --port INTEGER      Gateway port [default: 8765 or $GATEWAY_PORT]
  --force             Force stop (kill process)

Examples:
  praisonai gateway stop
  praisonai gateway stop --port 9000
  praisonai gateway stop --force

status

bash
praisonai gateway status [OPTIONS]

Options:
  --host TEXT         Gateway host [default: 127.0.0.1]
  --port INTEGER      Gateway port [default: 8765 or $GATEWAY_PORT]
  --daemon-only       Show only daemon status

Examples:
  praisonai gateway status
  praisonai gateway status --port 9000
  praisonai gateway status --daemon-only

What it reports

status probes /health and /info, then reports reachability and whether your credential is accepted.

LineMeaning
Access: authorized (scopes: read, write)/info returned 200; your token’s operator scopes are listed
Access: credential refused (HTTP 403)The gateway answered 401/403
Access: unknown (/info returned HTTP 500)No auth decision was made (timeout, 404, 5xx)
Next step: ...A follow-up action, when one applies
text
$ praisonai gateway status
Gateway: reachable at http://127.0.0.1:8765
  Access: authorized (scopes: read)
  Next step: credential lacks operator write/admin scope; grant it via
             gateway.auth_scopes in the gateway config
text
$ praisonai gateway status
Gateway: reachable at http://127.0.0.1:8765
  Access: credential refused (HTTP 403)
  Next step: GATEWAY_AUTH_TOKEN was rejected; it does not match the
             running gateway's auth token
Diagram

No answer

200 + scopes

401 / 403

other 4xx/5xx

No

Yes + no token

Yes + wrong token

praisonai gateway status

/info reachable?

Access omitted — reachable: false

Access: authorized

Access: credential refused

Access: unknown

Host loopback?

Next step: run on the gateway host

Next step: doctor –fix

Next step: token rejected

JSON output

--json adds an access block next to reachable.

json
{
  "reachable": true,
  "access": {
    "authorized": true,
    "scopes": ["read", "write"]
  }
}
FieldTypeWhen presentMeaning
authorizedtrue / false / nullAlways, inside accesstrue → /info returned 200; false → 401/403; null → no auth decision (timeout, 404, 5xx)
scopeslist[str]authorized is trueYour token’s effective operator scopes
http_statusint / nullauthorized is false or nullHTTP status /info returned, or null if nothing answered
next_stepstrWhen a follow-up appliesNamed action from the table below

access is omitted when reachable is false.

Situationnext_step
Authorized, but no write or admin scopecredential lacks operator write/admin scope; grant it via gateway.auth_scopes in the gateway config
401/403 on a non-loopback host`the CLI only sends GATEWAY_AUTH_TOKEN to loopback; run praisonai gateway status on the gateway host`
401/403 on loopback, no token found`no GATEWAY_AUTH_TOKEN found; export it or run praisonai gateway doctor –fix `
401/403 on loopback, token presentGATEWAY_AUTH_TOKEN was rejected; it does not match the running gateway's auth token

doctor

bash
praisonai gateway doctor [OPTIONS]

Options:
  -c, --config TEXT   Path to gateway.yaml [default: gateway.yaml — resolves to
                      the onboarded ~/.praisonai/bot.yaml when present]
  --json              Output JSON
  --channel TEXT      Channel a --turn probe uses (default: first configured)
  --turn TEXT         Run one offline inbound agent turn via
                      BotSessionManager.chat (requires LLM API key)
  --fix               Repair safe findings (mint a strong gateway auth token
                      when weak/missing), then re-validate
  --dry-run           With --fix, preview repairs without writing anything

Examples:
  praisonai gateway doctor
  praisonai gateway doctor --fix
  praisonai gateway doctor --fix --dry-run
  praisonai gateway doctor --config my-gateway.yaml --json
  praisonai gateway doctor --config gateway.yaml --channel slack --turn "Say OK"
  praisonai gateway doctor --config gateway.yaml --json --channel slack --turn "Say OK"

test

bash
praisonai gateway test [OPTIONS]

Options:
  -c, --config TEXT   Path to gateway.yaml [default: gateway.yaml — resolves to
                      the onboarded ~/.praisonai/bot.yaml when present]
  --json              Output JSON (single top-level document)
  --channel TEXT      Target channel for --turn (must exist in config)
  --turn TEXT         Offline inbound agent turn via BotSessionManager.chat
                      (requires LLM API key)
  --check-running     Probe REST /info on the running gateway to confirm
                      reachability

Examples:
  praisonai gateway test
  praisonai gateway test --config gateway.yaml
  praisonai gateway test --config gateway.yaml --channel slack \
    --turn "run uname -a with execute_command"
  praisonai gateway test --config gateway.yaml --check-running
  praisonai gateway test --config gateway.yaml --json

test combines credential probes with offline shell wiring validation, then layers optional checks (--turn, --check-running). See Gateway Readiness for the full three-tier checklist.

mint-link

bash
praisonai gateway mint-link [OPTIONS]

Options:
  --ttl INTEGER   Link time-to-live in seconds [default: 600]

Examples:
  praisonai gateway mint-link
  praisonai gateway mint-link --ttl 3600

mint-link prints a fresh pairing/magic link that binds a cross-platform identity to the gateway, and also writes it to ~/.praisonai/last-link.txt (mode 600). The link resolves against GATEWAY_HOST / GATEWAY_PORT (defaulting to 127.0.0.1:8765). It exits 1 when magic-link support is unavailable.

sessions

bash
praisonai gateway sessions <list|show|export|import> [OPTIONS]

Examples:
  praisonai gateway sessions list                       # inspect per-channel entries
  praisonai gateway sessions show bot_slack_U123 --tail 20
  praisonai gateway sessions export --out backup.json   # back up all sessions
  praisonai gateway sessions import --in backup.json    # restore (inert until re-bound)

sessions inspects — and can prune, back up, or restore — the per-channel session store so an operator can manage live conversations without opening the store by hand.

list / show:

SubcommandFlagsDescription
list--platform, --active SECONDS, --jsonList active/known sessions
show SESSION_REF--tail 20, --jsonShow one session’s last N transcript lines

export:

FlagTypeDefaultDescription
--session-id, -sstr?NoneExport just this session (lineage-aware); default = all
--out, -ostr?stdoutAtomic write via temp file + os.replace
--no-lineageboolFalseSkip compacted/rotated ancestors for a single-session export

import:

FlagTypeDefaultDescription
--in, -istrrequiredRead payload from this file
--overwriteboolFalseOverwrite sessions that already exist
--keep-live-fieldsboolFalseDo not reset live routing/activity fields
--max-sessionsint10_000Cap ingest
--jsonboolFalseOutput the ImportReport as JSON

See Gateway Session Portability for the full backup / migrate / restore story.

logs

bash
praisonai gateway logs [OPTIONS]

Options:
  -n INTEGER   Number of trailing lines to show

Examples:
  praisonai gateway logs
  praisonai gateway logs -n 100

logs tails the gateway’s stdout/stderr under the daemon runner (~/.praisonai/logs/bot-stderr.log on macOS; journalctl --user -u praisonai-bot on Linux).

send

bash
praisonai gateway send [OPTIONS]

Options:
  --channel TEXT       Channel to push through (e.g. telegram)
  --channel-id TEXT    Destination id on that channel
  -m, --message TEXT   Message body

Examples:
  praisonai gateway send --channel telegram --channel-id 12345 -m "test"

send pushes a one-off message through the outbound messenger so you can smoke-test delivery to a real channel without waiting for an inbound trigger.

channels

bash
praisonai gateway channels [OPTIONS] [SUBCOMMAND]

Options (list / probe when no subcommand is given):
  -c, --config TEXT   Path to gateway.yaml [default: gateway.yaml — resolves to
                      the onboarded ~/.praisonai/bot.yaml when present]
  --json              Output JSON format
  --probe             Probe each channel's credentials
  --available         List all registered platforms

Subcommands (edit one channel in place):
  add <name>      [--platform P] [--token T] [--set k=v]...
  update <name>   [--token T] [--set k=v]...
  disable <name>
  remove <name>

Examples:
  praisonai gateway channels                         # list
  praisonai gateway channels --probe                 # list + credential probe
  praisonai gateway channels add telegram            # token defaults to ${TELEGRAM_BOT_TOKEN}
  praisonai gateway channels add discord --set group_policy=mention_only
  praisonai gateway channels update telegram --token '${NEW_TOKEN}'
  praisonai gateway channels disable slack           # keep config, stop serving
  praisonai gateway channels remove slack            # delete the block

See Gateway Channel Lifecycle for every option and validation rule.

pause

bash
praisonai gateway pause <channel-name> [OPTIONS]

Options:
  --host TEXT   Gateway host [default: 127.0.0.1]
  --port INTEGER Gateway port [default: 8765 or $GATEWAY_PORT]
  --url TEXT    Gateway WebSocket URL — resolved from --host/--port when omitted

Examples:
  praisonai gateway pause telegram
  praisonai gateway pause telegram --port 9000        # non-default port
  GATEWAY_PORT=9000 praisonai gateway pause telegram  # via env
  praisonai gateway pause discord --url ws://localhost:8000  # explicit URL still wins

resume

bash
praisonai gateway resume <channel-name> [OPTIONS]

Options:
  --host TEXT   Gateway host [default: 127.0.0.1]
  --port INTEGER Gateway port [default: 8765 or $GATEWAY_PORT]
  --url TEXT    Gateway WebSocket URL — resolved from --host/--port when omitted

Examples:
  praisonai gateway resume telegram
  praisonai gateway resume telegram --port 9000        # non-default port
  GATEWAY_PORT=9000 praisonai gateway resume telegram  # via env
  praisonai gateway resume discord --url ws://localhost:8000  # explicit URL still wins

reconnect

bash
praisonai gateway reconnect <channel-name> [OPTIONS]

Options:
  --host TEXT   Gateway host [default: 127.0.0.1]
  --port INTEGER Gateway port [default: 8765 or $GATEWAY_PORT]
  --url TEXT    Gateway WebSocket URL — resolved from --host/--port when omitted

Examples:
  praisonai gateway reconnect telegram
  praisonai gateway reconnect telegram --port 9000        # non-default port
  GATEWAY_PORT=9000 praisonai gateway reconnect telegram  # via env
  praisonai gateway reconnect discord --url ws://localhost:8000  # explicit URL still wins

Config discovery

You no longer need --config if you have onboarded — praisonai gateway start discovers the config praisonai onboard wrote. Every gateway command (start, doctor, test, status, send, channels) uses the same canonical resolution order, so onboard, start, and doctor cannot drift apart.

bash
praisonai onboard          # writes ~/.praisonai/bot.yaml
praisonai gateway start    # discovers it — no flag needed
Diagram

Yes

No

Yes

No

Yes

No

Yes

No

praisonai gateway start

–config passed?

Use –config path

./bot.yaml exists?

Use ./bot.yaml

~/.praisonai/bot.yaml exists?

Use ~/.praisonai/bot.yaml

./gateway.yaml exists?

Use ./gateway.yaml alias

exit 78 — run praisonai onboard

PrecedenceSourceNotes
1--config <path> / --agents <path>Explicit path always wins
2./bot.yaml (cwd)Local project override
3$PRAISONAI_BOT_CONFIG / ~/.praisonai/bot.yamlEnv var — PRAISONAI_HOME-aware; where praisonai onboard writes
4./gateway.yaml (cwd)Backward-compatible alias — legacy name
—nonePrint onboard hint, exit 78

start with no config

praisonai gateway start (no --config, no --agents) discovers the onboarded config, prints Using gateway config: <path>, then binds with channels instead of the earlier silent WebSocket-only no-op. If nothing is found it prints a hint and exits 78:

bash
$ praisonai gateway start
No gateway config found. Run 'praisonai onboard' to create one, or pass
--config <path> (or --agents <path> for single-agent mode).
$ echo $?
78

Passing --agents <path> selects single-agent mode and skips channel-config discovery.

doctor / test / status / send / channels

These commands default --config to a gateway.yaml sentinel. When you leave it at the default, they run the same discovery order as start — so praisonai gateway doctor after praisonai onboard inspects the onboarded ~/.praisonai/bot.yaml, not a file onboarding never wrote. An explicit --config always wins.

Version validation

praisonai gateway start also validates the config’s config_version before binding, applying the same forward-migration doctor --fix uses. A config from a newer build refuses to start (exit 78) rather than being silently downgraded. See Gateway Config Migration › Config version stamp.

What the user sees

The whole reason this discovery exists: before, an onboarded operator who ran praisonai gateway start with no flag started silently channel-less, so their Telegram user got no reply.

Diagram
Telegram userGatewayOperatorTelegram userGatewayOperatorBefore — silent channel-less startAfter — safe by defaultpraisonai onboard (writes ~/.praisonai/bot.yaml)praisonai gateway startbound on 127.0.0.1:8765, silently channel-lessTelegram messageno reply — bot never joined the channelpraisonai onboard (writes ~/.praisonai/bot.yaml)praisonai gateway start_resolve_gateway_config_path -> ~/.praisonai/bot.yaml_ensure_config_current -> migrate if neededbound on 127.0.0.1:8765, telegram channel upTelegram messagereply

For a focused, standalone walkthrough of this resolution, see Gateway Config Discovery. Start-time version validation is documented in Config Migration → At start time.


Pre-flight credential check

praisonai gateway doctor validates every channel’s token before the gateway starts, so a bad or expired credential fails fast with a precise per-channel reason instead of disappearing into the supervisor’s silent reconnect loop.

Diagram

Pre-flight credential check

Yes

No

📄 gateway.yaml

🔍 probe_all

✓ Telegram getMe

✗ Slack auth.test

✓ Discord identify

📋 Per-channel verdict

All OK?

🚀 gateway start

❌ exit 1 — fix tokens

Examples

Quick health check:

bash
$ praisonai gateway doctor
telegram     ✓  @my_support_bot
slack        ✗  SSL certificate verify failed (network/proxy?). Token may still be valid. Try SSL_CERT_FILE=/path/to/corp-ca.pem or gateway start --no-preflight
discord      ✓  @MySupport

A non-SSL failure keeps the bare error string:

bash
$ praisonai gateway doctor
telegram     ✓  @my_support_bot
slack        ✗  invalid_auth (token expired)
discord      ✓  @MySupport

CI-friendly JSON — a single document with a probes block (and a secrets block when any channel uses a secret reference):

bash
$ praisonai gateway doctor --json
{
  "probes": {
    "telegram": {"ok": true,  "platform": "telegram", "bot_username": "my_support_bot"},
    "slack":    {"ok": false, "platform": "slack",    "error": "invalid_auth"},
    "discord":  {"ok": true,  "platform": "discord",  "bot_username": "MySupport"}
  },
  "secrets": {
    "telegram": {"token": "available"},
    "slack":    {"token": "available", "app_token": "configured-but-unavailable"},
    "discord":  {"token": "configured"}
  }
}

Same verdict via the listing command:

bash
$ praisonai gateway channels --probe

Credential availability (without revealing values)

praisonai gateway doctor also prints a per-channel credential availability table so operators can validate secret wiring — including the secret-reference form on token, app_token, and verify_token — without ever printing a value.

bash
$ praisonai gateway doctor
Credential availability (values never shown):
telegram     token         ✓  available
slack        token         ✓  available
slack        app_token     ✗  configured-but-unavailable
whatsapp     verify_token  ✗  missing
StateMeaning
availableResolved successfully (env set, file present and non-empty)
configured-but-unavailableConfigured but cannot resolve (empty file, unreadable, empty env var)
configuredAn exec reference — configured, but not executed at probe time
missingNot set (env var unset, file not found)

An exec-sourced reference reports configured without running its command: the command has side effects (a one-shot / rate-limited / rotating secret-manager call) and the network probe resolves the same reference moments later, so executing it here would run it twice.

These states now map cleanly to runtime isolation at boot — a configured-but-unavailable or missing channel credential isolates just that channel as degraded rather than aborting the gateway. See Degraded Channel Isolation.

After boot, GET /health emits the same degraded / reason: "credential unavailable" verdict for a channel whose token is rejected at runtime (401/403, revoked/rotated/expired) — no new flags. See Runtime credential rejection.

The --json output is a single document with probes and secrets keys:

json
{
  "probes":  { "telegram": {"ok": true }, "slack": {"ok": false} },
  "secrets": { "telegram": {"token": "available"},
               "slack":    {"token": "available", "app_token": "configured-but-unavailable"} }
}

Weak-secret check

gateway doctor also flags the gateway’s own auth_token when it matches a well-known placeholder — fails on an external bind, warns on loopback.

On an external bind with a weak token, doctor prints the full refusal message and exits 1:

bash
$ praisonai gateway doctor --config gateway.yaml
Refusing to start: gateway.auth_token is a known-weak/placeholder value.
A publicly-known secret provides no real authentication.
Fix:  praisonai onboard         (30 seconds, 3 prompts)
Or:   export GATEWAY_AUTH_TOKEN="$(openssl rand -hex 16)"  (run in a shell so the command is expanded, not pasted literally)

On a loopback bind, doctor prints a warning and still exits 0:

bash
$ praisonai gateway doctor --config gateway.yaml
⚠  gateway.auth_token is a known-weak/placeholder value (loopback bind 127.0.0.1). Rotate before exposing externally.

A strong token prints nothing extra — silence is a pass, consistent with the credential-availability table.

The --json output gains a top-level gateway_auth_token key (present only when the verdict is weak), alongside probes and secrets. When --fix is passed, an optional fix key carries the repair report:

json
{
  "probes":  { "telegram": {"ok": true} },
  "secrets": { "telegram": {"token": "available"} },
  "gateway_auth_token": "weak",
  "fix": "gateway_auth_token: weak → generated a strong token… done\nre-validated: gateway_auth_token now strong"
}
VerdictBindExit code
Weak / missing auth_tokenExternal1
Weak auth_tokenLoopback0 (warning only)
Strong auth_tokenAny0
Weak Secret Guard

Full denylist, bind-aware behaviour matrix, and how to fix a weak change-me-style token

Auto-repair with --fix

--fix repairs safe findings, then re-validates that each finding cleared.

Diagram

detect → repair → re-validate

🔍 Detect weak/missing auth_token

🔑 Mint token_hex 16

💾 Write ~/.praisonai/.env

📄 Rewrite explicit weak YAML

🔁 Re-run strength check

✅ now strong — exit 0

❌ still weak — exit 1

Repair a weak token, then confirm it cleared:

bash
$ praisonai gateway doctor --config gateway.yaml --fix
gateway_auth_token: weak → generated a strong token… done
re-validated: gateway_auth_token now strong

Preview the repair without writing anything:

bash
$ praisonai gateway doctor --config gateway.yaml --fix --dry-run
gateway_auth_token: weak → would mint a strong token (--dry-run)
Finding--fix actionPersistence
gateway.auth_token weak/missingMint secrets.token_hex(16), export to env, persist to ~/.praisonai/.envSurvives daemon restart
gateway.auth_token explicit weak in YAMLAlso rewrites gateway.yaml in place (preserves ${ENV} references)Config file updated
Any other findingNot repaired yet (no dead-end: still surfaced with its own retry_hint)—

--dry-run still exits 1 when a repairable finding is present, so CI catches un-fixed states before anything is written.

Pre-flight gate on start

praisonai gateway start runs the same probe automatically before launch when invoked with --config gateway.yaml. By default a bad token isolates only that channel — every healthy channel keeps serving:

bash
# Default: isolate the failing channel, serve the rest
$ praisonai gateway start --config gateway.yaml
telegram     ✗  invalid_auth (token expired)
slack        ✓
discord      ✓

Pre-flight: 2 channel(s) OK; skipping telegram as configured-unavailable
(auto-recovers when the credential is fixed). See `gateway status`.

Opt into strict mode to restore the old fail-closed behaviour — or preflight fails closed on its own when no channel is serviceable:

bash
# --strict-preflight (opt-in) or when NO channel is serviceable: fail closed
$ praisonai gateway start --config gateway.yaml --strict-preflight
telegram     ✗  invalid_auth (token expired)
slack        ✓

Pre-flight check failed — aborting start (--strict-preflight set). Fix the
channel credentials above, or pass --no-preflight to skip /
--no-strict-preflight to isolate the degraded channel and serve the rest.

To bypass probing during local dev (e.g. flaky probe network):

bash
praisonai gateway start --config gateway.yaml --no-preflight

Route/binding target check

praisonai gateway doctor also validates that every routes, routing, and bindings.agent target names an agent declared in the sibling agents: map. A typo — including in the default slot — fails with a “did you mean X?” hint instead of silently misrouting traffic.

bash
$ praisonai gateway doctor --config gateway.yaml
ERROR: Invalid gateway/bot configuration:
channel 'telegram' route 'dm' -> unknown agent 'personl'; did you mean 'personal'? valid agents: personal, support

This runs only for multi-agent configs (an agents: map is present). Single-bot configs (top-level platform + token) are unaffected. See Fail-Fast Validation for the full behaviour.

Corporate CA bundle (SSL-inspecting networks)

On networks that intercept TLS with a corporate CA (proxy / MITM), the probe’s HTTP client can reject the certificate chain even though the token is valid and the runtime adapter connects fine. Preflight classifies these SSL cert-verify failures separately and soft-fails when they are the only failures.

bash
$ praisonai gateway start --config gateway.yaml
slack        ✗  SSL certificate verify failed (network/proxy?). Token may still be valid. Try SSL_CERT_FILE=/path/to/corp-ca.pem or gateway start --no-preflight

Pre-flight found SSL certificate-verify failures only (likely a proxy/MITM
network). Tokens may still be valid — continuing start. Set SSL_CERT_FILE /
REQUESTS_CA_BUNDLE / PRAISONAI_SSL_CA_BUNDLE to your corporate CA, or pass
--no-preflight to skip this check.

The probe decides its action from the mix of failures and whether strict mode is set:

Probe outcome (across all channels)strict off (default)strict on
All channels okStart proceeds silentlyStart proceeds silently
≥1 channel fails with a non-SSL error, ≥1 healthyIsolate the failing channel(s) as CREDENTIAL_UNAVAILABLE, serve the rest, exit 0Hard-abort, exit 1
Every channel fails with a non-SSL error (no healthy channel)Hard-abort (nothing left to serve), exit 1Hard-abort, exit 1
All failures are SSL cert-verify onlySoft-fail — print the SSL warning, continue to start, exit 0Soft-fail (unchanged)
Mixed: ≥1 SSL cert-verify and ≥1 non-SSL failureIsolate the non-SSL failure(s), SSL warning printed, exit 0 if any channel remains healthyHard-abort, exit 1

Point the probe at your corporate CA with one of three env vars, highest precedence first:

VariablePrecedenceBehaviour
PRAISONAI_SSL_CA_BUNDLEHighest — explicit PraisonAI overrideOverrides pre-existing SSL_CERT_FILE and REQUESTS_CA_BUNDLE for the probe.
REQUESTS_CA_BUNDLEMiddleUsed only if PRAISONAI_SSL_CA_BUNDLE is unset. Left alone when the winner came from elsewhere.
SSL_CERT_FILELowest — Python defaultRead by Python’s default ssl context (used by aiohttp). Used only if the two above are unset.
bash
export PRAISONAI_SSL_CA_BUNDLE=/path/to/corp-ca.pem
praisonai gateway start --config gateway.yaml

A configured-but-missing path warns and leaves the SSL env vars untouched:

Warning: CA bundle path '<path>' does not exist — SSL_CERT_FILE / REQUESTS_CA_BUNDLE not updated for probe.
Diagram

Preflight decision

Yes

No

No

Yes

Yes

No

❌ Probe failure

Only SSL cert-verify?

⚠️ Warn + continue

Any healthy channel?

⛔ Abort — nothing to serve

–strict-preflight
or preflight.strict?

⛔ Abort — strict mode

🛡️ Isolate degraded,
serve healthy — exit 0

Token resolution

The probe loads ~/.praisonai/.env first, so ${VAR} placeholders set by praisonai onboard resolve exactly like they do at runtime — doctor, channels --probe, and start --preflight all share the same token-resolution path.

Per-channel timeout

Each probe is bounded by a 15-second deadline; a stuck adapter is reported as "probe timed out after 15s" and does not hang the aggregate.

Exit codes

OutcomeExit code
All channels probe OK0
Any channel fails1
Weak / missing gateway auth_token on an external bind1
Weak gateway auth_token on a loopback bind (warning only)0
gateway doctor with no channels configured0

When to use which

Diagram

Just-in-time health check

Already using channels list?

About to start the gateway?

Programmatic healthcheck endpoint

Need to check credentials?

praisonai gateway doctor

praisonai gateway channels –probe

Default –preflight runs the same check

BotOS.probe_all

Programmatic check (Python)
python
import asyncio
from praisonai.bots import Bot, BotOS

botos = BotOS(
    bots=[
        Bot("telegram", token="..."),
        Bot("slack",    token="..."),
    ],
    enable_supervision=False,
)

results = asyncio.run(botos.probe_all(timeout=15.0))
for platform, r in results.items():
    print(platform, "ok" if r.ok else r.error)

Pre-flight tool check

praisonai gateway start validates every tool named in your config before the gateway binds, so a typo or an uninstalled optional package fails fast with a per-name reason instead of silently starting a quietly under-powered bot.

This gate runs one layer above the core resolver’s PRAISONAI_STRICT_TOOLS / ToolResolutionError mechanism — it fires at start-time, before GatewayHandler.start() builds any agent. See Tool Resolution for the complementary core-resolver behaviour.

Diagram

Tool pre-flight

Yes

No

strict default

–no-strict-tools
or YAML: false

📄 gateway.yaml

🔍 validate_yaml_tools

All resolve?

🚀 gateway start

📋 Per-name reason
+ ‘did you mean?’

strict_tools?

❌ exit 78

⚠️ Warn + continue

Strict mode (default) — fail fast

bash
$ praisonai gateway start --config gateway.yaml
✗ Tool pre-flight failed:
    - 'duckduckgo_serch' not found. Did you mean 'duckduckgo'?
  Fix the names in your config, or start with --no-strict-tools to run without them.
$ echo $?
78

Non-strict — warn and continue

Two equivalent ways:

CLI flag

bash
praisonai gateway start --config gateway.yaml --no-strict-tools

YAML key

yaml
# gateway.yaml
strict_tools: false
agents:
  assistant:
    instructions: "..."
    tools: [duckduckgo_serch]        # unresolved but tolerated

Either produces:

⚠ Tool pre-flight (non-strict) — starting without:
    - 'duckduckgo_serch' not found. Did you mean 'duckduckgo'?

“Did you mean?” mechanics

Suggestions use stdlib difflib.get_close_matches (cutoff 0.7, one suggestion per unknown). A tool that is registered but currently unloadable (e.g. an optional package not installed) is excluded from its own candidates, so it yields an install hint rather than suggesting the same name back:

'firecrawl' not found in any source. Check the spelling, install the package
that provides it, or set PRAISONAI_ALLOW_LOCAL_TOOLS=true for a local tools.py.

A project tools.py that exists but is gated by the local-tools env var is flagged explicitly:

'my_local' not found. A local tools.py exists but local tools are disabled;
set PRAISONAI_ALLOW_LOCAL_TOOLS=true to enable it.

An unknown toolset name is reported the same way:

- toolset:research not found (unknown toolset)

Precedence

SettingResult
YAML strict_tools: falseGate disabled (regardless of CLI flag)
CLI --no-strict-toolsGate disabled for this invocation
CLI --strict-tools (default) with no YAML overrideGate strict → fail fast on any unresolved tool
No unresolved toolsPre-flight is silent, start proceeds

YAML strict_tools: false opts a bot out permanently; --no-strict-tools is per-invocation. A strict CLI default does not override a YAML strict_tools: false.

Env loading

The gate loads ~/.praisonai/.env before resolving, so PRAISONAI_ALLOW_LOCAL_TOOLS=true stored there by praisonai onboard is honoured and a gated local tools.py is not falsely reported as unresolved. The load is idempotent and existing process env wins — GatewayHandler.start() re-loads the same file moments later.

Exit code

Strict failure exits with 78 (EX_CONFIG), which the OS units generated by praisonai gateway install treat as do-not-restart — an unresolvable tool in the config would loop the daemon otherwise. See Gateway Exit Codes.


Environment Variables

VariableDescriptionDefault
GATEWAY_PORTPort for start/stop/status when –port is not passed8765

Supervisor exit codes

praisonai gateway start returns a supervisor-friendly exit code so a service manager (launchd / systemd / a scheduled task) knows whether to restart the gateway or stop and wait for an operator.

CodeMeaningWhen you see it
0Clean shutdown / successCtrl+C, SIGTERM drain, stop, a healthy status
75Transient failure — supervisor should restartNetwork blip, port contention, transient IO
78Fatal configuration error — supervisor should stopBad gateway.yaml, no platforms, duplicate token, unresolved strict tool

The installed OS unit maps 78 (EX_CONFIG) to do-not-restart so a broken config cannot crash-loop the daemon, and treats 75 (EX_TEMPFAIL) as restartable. See Gateway Exit Codes for the full contract and per-platform wiring.


Single-Instance Enforcement

PraisonAI enforces two locks at gateway start: a port lock (one gateway per host:port) and a state lock (one gateway per shared state root, ~/.praisonai). At most one gateway may own ~/.praisonai at a time, whatever port it uses.

Diagram

Yes

No

Acquired

Conflict

Acquired

Conflict

🚀 Gateway Start

🔍 Port Available?

🔒 Port Lock?

❌ Port Error

🔒 State Lock?

❌ Instance Error

✅ Start Gateway

❌ State Owned

💡 Free the Port or Pick Another

💡 Stop Existing Gateway

💡 Isolated HOME + Port

LockFileKeyed on
Port lock~/.praisonai/gateway-<safe_host>-<port>.pidhost + port
State lock~/.praisonai/gateway.state.pidhome root (~/.praisonai)

The safe_host replaces : and . with _ (so 127.0.0.1 becomes 127_0_0_1).

State lock file

Two gateways on different ports that share ~/.praisonai would poll the same bot tokens and write the same outbox, DLQ, pairing and hook stores. That causes Telegram 409 errors and duplicate delivery, so the second gateway is refused:

Error: another gateway already owns the state at /home/user/.praisonai (PID 12345 on 127.0.0.1:8765).

  Two gateways sharing state poll the same bot tokens and write the same
  queues, causing 409 Conflict errors and duplicate delivery.
  Stop it:  praisonai gateway stop
  Or run an isolated gateway with its own state and bot tokens:
    HOME=/srv/gateway-b praisonai gateway start
  • Path: ~/.praisonai/gateway.state.pid, with a sidecar gateway.state.pid.guard.
  • Keyed on the home root. The state always lives at ~/.praisonai. Changing PRAISONAI_HOME or the port does not move it, so a second gateway with only a different PRAISONAI_HOME or port is still refused.
  • Held for the whole run. Claimed right after the port lock — or, when starting from gateway.yaml, before channels start (before the inbound journal is replayed). Released only after channels stop; a failed start releases it so restarts are not blocked.
  • Exclusive claim. A POSIX flock on the .guard file serialises the check, and the lock is created with os.link (falling back to O_EXCL), so two simultaneous starts can never both win. The port lock uses the same claim.
  • Stale reclaim. Uses the same PID + start-time check as the port lock, so a lock left by a crashed gateway is reclaimed on the next start.
  • Same-process safe. Re-claiming from the same PID succeeds, so in-process restarts are not blocked.

Run a second, isolated gateway

To run two gateways on one host, give the second one its own HOME (so its own ~/.praisonai state), its own bot tokens and its own port:

bash
# Shell 1 — default gateway
praisonai gateway start

# Shell 2 — isolated gateway with its own state and bot tokens
HOME=/srv/gateway-b GATEWAY_PORT=8766 praisonai gateway start
I changed PRAISONAI_HOME but still get the state collision

PRAISONAI_HOME does not move the gateway state — the lock is keyed on ~/.praisonai. Set HOME= instead.

Both isolated gateways still see 409 from Telegram

The state is isolated but both gateways use the same bot token. Each gateway needs its own bot and token.

PID-Lock Status Reference

praisonai gateway status prints one PID-lock line for the port lock before the /health probe (the state lock is not shown). Look up any line you see:

Line printedWhen
Gateway PID lock: Process <pid> running (<host>:<port>)Lock file exists, PID responds to os.kill(pid, 0) and the recorded start-time fingerprint matches
Gateway PID lock: Stale lock (process <pid> not running)Lock file exists, but the PID no longer responds or its start time no longer matches the fingerprint (including Windows SystemError/ValueError cases)
Gateway PID lock: No lock file foundNo lock file at ~/.praisonai/gateway-<safe_host>-<port>.pid
PID lock status: Utilities not availableport_utils import failed (ImportError)
PID lock status: Unavailable (<error>)Any other unexpected exception during PID-lock inspection; the /health probe still runs

The port line (Port <host>:<port>: In use / Available) follows the same section unless PID-lock inspection fails, in which case the single PID lock status: Unavailable (<error>) line replaces both.


Restart the Gateway

Use praisonai gateway restart — it drains in-flight turns, then relaunches (via the installed service manager when present, otherwise directly).

bash
# Simple restart with default 10s drain
praisonai gateway restart

# Restart pointing at a config (needed only for the direct-relaunch fallback)
praisonai gateway restart --config gateway.yaml

# Longer drain window for slow agent turns
praisonai gateway restart --drain-timeout 30

# Restart a gateway running on a non-default endpoint
praisonai gateway restart --host 127.0.0.1 --port 9000
# equivalently:  GATEWAY_PORT=9000 praisonai gateway restart

Options

FlagTypeDefaultDescription
--hoststr127.0.0.1Gateway host (used to locate the running instance)
--portintGATEWAY_PORT env, else 8765Gateway port
--configstrnonePath to gateway.yaml for direct-relaunch fallback
--agentsstrnonePath to agent configuration file for direct-relaunch fallback
--drain-timeoutOptional[float]_persisted start value, else 10.0_Seconds to wait for in-flight agent turns before relaunch. Omit to replay the persisted start value; explicit value wins over the persisted one and applies to both the OLD-process drain and the relaunch.

Persisted start-flags artefact

praisonai gateway start persists the CLI-only runtime flags it was launched with to ~/.praisonai/gateway.start.<host>.<port>.json so a later praisonai gateway restart (direct, non-service path) reproduces the exact posture instead of silently reverting to defaults.

Diagram
sequenceDiagram
    participant Op as Operator
    participant Start as gateway start
    participant Disk as gateway.start.host.port.json
    participant Restart as gateway restart

    Op->>Start: gateway start --openai-api --reliability production --max-concurrent-runs 8
    Note over Start: validate config / agents / bind slot
    Start->>Disk: write { openai_api, reliability, max_concurrent_runs }
    Start-->>Op: bound on 127.0.0.1:8765

    Op->>Restart: gateway restart
    Restart->>Disk: load persisted flags
    Restart->>Start: relaunch (persisted, drain = persisted OR 10.0)
    Start-->>Op: same posture, no silent revert

    classDef op fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef cli fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef disk fill:#6366F1,stroke:#7C90A0,color:#fff

    class Op op
    class Start,Restart cli
    class Disk disk

Path: ~/.praisonai/gateway.start.<safe_host>.<port>.json (or $PRAISONAI_HOME/gateway.start.<safe_host>.<port>.json when set). safe_host replaces : with _, so IPv6 hosts work. The artefact is keyed by host:port — multiple gateways on one machine each keep their own posture.

Persisted keys (only non-None values are stored — None means “fall back to YAML”):

KeyCorresponds to
agent_file--agents
config_file--config
drain_timeout--drain-timeout
max_concurrent_runs--max-concurrent-runs
queue_depth--queue-depth
overflow_policy--overflow-policy
reliability--reliability
openai_api--openai-api
mcp--mcp
identity_store--identity-store
scale_to_zero--scale-to-zero
idle_minutes--idle-minutes
drain_marker--drain-marker

Write timing: the artefact is written only after startup validation passes (config parse, agent-file parse, admission wiring), immediately before the gateway binds. A start attempt that fails validation on the same host:port never clobbers the running gateway’s saved posture — otherwise the next restart would faithfully replay a rejected attempt.

Precedence on restart:

Diagram

Yes

No

Yes

No

praisonai gateway restart

Flag passed
explicitly?

Explicit wins

Persisted
value present?

Replay persisted

Static default
drain_timeout 10.0
others fall back to YAML

An omitted --drain-timeout on restart no longer forces a fixed 10s window; it replays the persisted value (and only falls back to 10.0 when nothing is persisted). An explicit --drain-timeout still wins over the persisted value for both the OLD-process drain and the relaunch, so a long configured drain is not silently cut off. Fixes #3349.

Advanced: OS-native restart

If you need to bypass the CLI (e.g. from a systemd ExecStartPre), the same platform commands the daemon dispatcher uses are:

macOS

bash
launchctl kickstart -k gui/$(id -u)/ai.praison.bot

Linux

bash
systemctl --user restart praisonai-bot

Windows

bash
schtasks /End /TN PraisonAIGateway && schtasks /Run /TN PraisonAIGateway

Status Output Examples

Healthy Gateway

bash
$ praisonai gateway status
Gateway PID lock: Process 12345 running (127.0.0.1:8765)
Port 127.0.0.1:8765: In use
Daemon service: Running (launchd)
Process ID: 12345
Gateway server: Reachable at http://127.0.0.1:8765/health
  Status: healthy
  Uptime: 3600.5 seconds
  Agents: 2
  Sessions: 1
  Clients: 3
  Channels: 2 configured

PID Lock Unavailable (Windows)

bash
$ praisonai-bot gateway status --port 18789
PID lock status: Unavailable (kill returned a result with an exception set)
Gateway Status: healthy
  Uptime: 11.4s
  Agents: 1
  Sessions: 0
  Clients: 0

Degraded Owners

praisonai gateway status prints a Degraded: section whenever health()["degraded_owners"] is non-empty. It is always printed (not gated on --deep) — an at-a-glance status must reveal a degraded state.

text
$ praisonai gateway status
...
  Degraded:
    ✗ gateway:durability:idempotency — durable idempotency store unavailable (running in-memory)  fix: praisonai gateway doctor --fix
    ✗ channel:slack — credential unavailable  fix: praisonai gateway doctor --fix

Each line follows the format ✗ <owner_kind>:<owner_id> — <reason> fix: <retry_hint>. The fix: suffix is omitted when retry_hint is empty. A durable webhook-idempotency or session store that fell back to in-memory shows here as gateway:durability:idempotency / gateway:durability:session; run praisonai gateway doctor --fix to repair, or a hot-reload that fixes the store path clears the entry automatically. See Gateway State Durability and Degraded Capabilities.

Daemon Issues

bash
$ praisonai gateway status
Daemon service: Installed but not running (launchd)
Gateway not reachable at http://127.0.0.1:8765/health

Not Installed

bash
$ praisonai gateway status --daemon-only
Daemon service: Not installed (systemd)

Platform Support

Gateway CLI works across platforms with native daemon integration:

PlatformService TypeLog LocationManagement
macOSLaunchAgent (ai.praison.bot)~/.praisonai/logs/bot-stderr.loglaunchctl kickstart -k gui/$(id -u)/ai.praison.bot
Linuxsystemd user service (praisonai-bot)journalctl --user -u praisonai-botsystemctl --user restart praisonai-bot
WindowsScheduled Task (PraisonAIGateway)Windows Event Logschtasks /End /TN PraisonAIGateway && schtasks /Run /TN PraisonAIGateway

Configuration Files

agents.yaml

yaml
# Simple agent configuration
agent:
  name: "support"
  instructions: "You are a helpful support agent"
  model: "gpt-4o-mini"
  tools:
    - search_web
  memory: true

gateway.yaml

yaml
# Multi-bot configuration
identity:
  enabled: true                       # one session per paired user across channels (#3020)
  store: ~/.praisonai/identity.json   # optional; ~ expanded, `path:` accepted as alias

# Opt into the pre-2026-07-31 warn-and-continue behaviour permanently.
# Omit (or set true) to fail fast on any unresolved tool at start (#3553).
strict_tools: false

agents:
  support:
    instructions: "You are a support agent"
    model: "gpt-4o-mini"
  sales:
    instructions: "You are a sales agent"
    model: "claude-3-5-sonnet-20241022"

channels:
  telegram:
    token: "${TELEGRAM_BOT_TOKEN}"
    routing:
      default: support
  discord:
    token: "${DISCORD_BOT_TOKEN}"
    routing:
      default: sales
  slack:
    # Secret-reference form: read from a mounted file / secret manager
    # instead of plaintext or a process-wide env var.
    token: { source: file, id: /run/secrets/slack_token }
    app_token: { source: exec, id: "vault read -field=token secret/slack" }
    routing:
      default: support

See Secret References for the full { source, id } form on token, app_token, and verify_token.


Common Patterns

Diagnose → repair → verify

Inspect first, repair only if needed, then confirm the gateway came back healthy.

bash
praisonai gateway doctor          # read-only report
praisonai gateway doctor --fix    # apply safe repairs, then re-validate
praisonai gateway status --deep   # per-channel rows + reload state

Zero-downtime posture change

restart drains in-flight turns for the given window, then relaunches — replaying the launch posture saved in ~/.praisonai/gateway.start.<host>.<port>.json, so the process comes back with the same flags it was started with.

bash
praisonai gateway restart --drain-timeout 60

Add an inbound trigger

Register a webhook that hands inbound events to an agent and delivers the reply to a channel.

bash
praisonai gateway hooks add gmail --agent triage-agent --deliver-to slack:C123
praisonai gateway hooks list

Best Practices

Use daemon-only for health checks

Use --daemon-only flag when monitoring daemon status in scripts or CI/CD pipelines to avoid gateway connection attempts.

Check logs for troubleshooting

Always check praisonai gateway logs when the daemon is running but gateway is unreachable – this reveals startup errors.

Test channel configuration

Use praisonai gateway send to test channel bot configuration before deploying to production environments.

Monitor daemon status regularly

Set up monitoring that runs praisonai gateway status --daemon-only to detect service failures quickly.

Set a runtime flag once — every restart replays it

Stop, restart with the new flag once, and every subsequent praisonai gateway restart replays it automatically — ~/.praisonai/gateway.start.<host>.<port>.json is your on-host record of what the process was launched with. To reset, delete the artefact.

Reset to YAML defaults by deleting the artefact

rm ~/.praisonai/gateway.start.<safe_host>.<port>.json before the next restart to revert to the YAML defaults (or an empty CLI baseline). A corrupt or unreadable artefact is treated as absent — the restart falls back to explicit flags and defaults.


Related