Skip to content

phpbotscout serve

Runs phpbotscout as a long-lived process. It reads the public channels of every configured server, answers the questions addressed to it, keeps the knowledge index current on a timer, and purges stored questions and answers as they expire. It posts an answer only in a server whose post_answers is set, and records it everywhere; see run the bot.

Usage

$ phpbotscout serve

Managed services

Services register with controls rather than being hand-rolled goroutines, so startup ordering, health and shutdown are the supervisor's job and not each service's.

Service What it does Restart policy
discord Holds the one gateway connection and reads every configured server's public channels, minus any excluded. Backs off, by the discord.restart keys: 10 restarts, 2s initial, 5m maximum by default
messaging The in-process bus that carries each message and reaction from the gateway to the answering path. None
health The /healthz, /livez and /readyz listener. None
index-refresher Re-reads the corpus on a timer. None
question-purge Deletes questions after 180 days and answer text after 120, once a day. None

The connection backs off because a gateway that cannot connect will not connect harder for being asked more often, and Discord rate-limits identify. The refresher has no policy because its Start cannot fail, and the failure it does have (a forge that will not answer) is not fixed by restarting anything.

The scheduled refresh

The refresher calls the same Refresh that index refresh calls. One code path, so the scheduled and manual routes cannot drift into disagreeing about what is indexed.

It refreshes once at startup and then on every tick of index.refresh_interval. Refreshing at startup rather than waiting a full interval means a daemon restarted after downtime notices the world moved on promptly. Startup does not wait for that first refresh: indexing the corpus takes minutes, and a supervisor waiting on it would report the daemon as failing to come up while it was working correctly.

Two refreshes never overlap. If one is still running when the next tick arrives, that tick is skipped rather than queued: two concurrent runs would write the same documents in parallel.

Failure is degradation, not an outage

A refresh that fails is counted and logged, and the daemon keeps running.

  • Liveness always passes. A restart would not reach an unreachable forge.
  • Readiness does not fail on a stale index. Answering from what is already indexed, with a caveat, beats not answering, and taking the bot out of service would fix nothing. This is the same judgement the gateway makes about a reconnect that lost events.

Staleness is reported through index status, which is where an operator can act on it, and as a count on /healthz: the index-staleness check is DEGRADED with stale sources: N while any source is stale, which leaves the endpoint healthy.

A refresh that the forge rate-limits stops early and logs index refresh stopped early. The sources it did not reach keep their index and are tried again next tick. See index refresh.

A source that fails while the rest of the run succeeds is logged by name with its reason. The run itself reports success, so that log line is the only signal that part of the corpus went unindexed.

Shutdown

Stop waits for a refresh already in flight. Returning immediately would let the process exit between writing a document and recording the commit it came from, leaving the index claiming to hold content it does not. The wait is bounded by the shutdown deadline; past it the refresh is cancelled and its transaction rolls back.

Startup refuses a bad configuration

Building the forge provider makes no network call, so it can only fail on a configuration an operator can fix in seconds: a malformed API URL, an index path that cannot be opened, a provider that cannot enumerate a namespace. Those fail startup, in keeping with how the gateway's own configuration is treated.

A forge that is merely unreachable is a different thing entirely. That is not known at startup, surfaces later as a failed refresh, and degrades rather than stopping the daemon.

Health endpoints

serve mounts /healthz, /livez and /readyz on the health listener, which binds 127.0.0.1:8081 by default. Wildcard binds are refused.

Flags

serve defines no flags of its own.

Name Description Default
-h, --help Help for serve. false

Global flags (--config, --debug, --output, --ci, --accessible) are inherited from the root command.

Example

$ phpbotscout --config ./config.yaml serve