DEV Community

John Wick
John Wick

Posted on

StayPresent v1.6.0: Status Dashboard, Hang Detection, and Flexible Deployment Modes

What's new in staypresent v1.6.0 — a built-in status page, heartbeat hang detection, bot-less/web-less run modes, and static file exclusions.

StayPresent v1.6.0: Status Dashboard, Hang Detection, and Flexible Deployment Modes

StayPresent v1.6.0 is the biggest release the package has had, adding a genuinely new category of capability rather than just tuning existing behavior: a built-in status dashboard, hang detection for bots that are technically running but actually frozen, and new deployment modes that decouple the web server from the bot entirely. Here's a tour of what's new, with links to deeper coverage of each feature.

Table of Contents

  1. The Built-In Status Page
  2. Hang Detection with heartbeat()
  3. New Run Modes: Bot-Only and Web-Only
  4. File Exclusions for Static Routes
  5. Status Page Customization Per Route
  6. Captured Bot Output
  7. Notable Bug Fixes
  8. Behavioral Changes Worth Knowing About
  9. Upgrading
  10. FAQs
  11. Conclusion

The Built-In Status Page

The headline feature: staypresent.web.status() generates a real, auto-updating status page in a single function call — a per-service list, rolling uptime figures (24h, 7d, 30d, and lifetime), and incident history pulled directly from what StayPresent already tracks internally.

staypresent.web.status(
    title="Groundflare Bot Status",
    copyright="Groundflare Inc.",
    footer_links=[{"label": "Support", "url": "https://support.groundflare/support"}],
)
Enter fullscreen mode Exit fullscreen mode

You don't even need to call it explicitly — a status page is now served at /status by default, right alongside /health and /. The function call is only needed if you want to customize the title, footer, or move it to a different path.

Public visitors see overall status, uptime, and friendly incident descriptions. Admin-level detail — exit codes, recent stdout/stderr tails — sits behind an api_key-gated login, rate-limited to 5 attempts per 15 minutes. If you don't set a key, StayPresent generates a random one per session and logs it; pass api_key="" to disable the admin view entirely.

Hang Detection with heartbeat()

Crash detection has always caught a bot that actually exits. It's never been able to catch a bot that's technically still running but completely stuck — a deadlock, a frozen loop, a hung network call that never times out. staypresent.heartbeat() fixes this:

# worker.py
import staypresent

while True:
    staypresent.heartbeat()
    do_work()
Enter fullscreen mode Exit fullscreen mode
# app.py
import staypresent

staypresent.run("worker.py", heartbeat_timeout=30)
Enter fullscreen mode Exit fullscreen mode

If the worker stops calling heartbeat() for longer than heartbeat_timeout seconds, StayPresent logs the issue, terminates the process, and handles it exactly like a regular crash — restart logic and all.

New Run Modes: Bot-Only and Web-Only

Two new modes decouple the bot and the HTTP server, which previously always ran together:

Bot supervision, no HTTP server at all:

staypresent.run("bot.py", web_server=False)
Enter fullscreen mode Exit fullscreen mode

HTTP server only, no bot:

staypresent.run()
Enter fullscreen mode Exit fullscreen mode

The second case is genuinely new — StayPresent can now be a standalone status/health-check service with nothing running underneath it, useful for a dedicated monitoring deployment separate from the bots it's watching.

File Exclusions for Static Routes

web.html() and web.markdown() now accept exclude to block specific files, extensions, or glob patterns from being served alongside the target file:

staypresent.web.html(
    "templates/index.html",
    exclude=[".env", ".git", "*.py", "secrets.json"],
)
Enter fullscreen mode Exit fullscreen mode

Excluded requests return a clean 404. This directly addresses the "whole directory is exposed" caveat that existed in earlier versions — see the security-focused deep dive linked at the end of this post.

Status Page Customization Per Route

Every route-registering function — text(), json(), html(), markdown(), and the new status() — now accepts status=True/False to control whether it gets its own row on the status page. Bots are shown by default unless explicitly opted out, and services_name/services_description let you rename or describe a row instead of showing a raw path.

Captured Bot Output

Bot stdout/stderr are now captured into a ring buffer rather than only being echoed to the console — still visible live in the parent process as before, but now also feeding the status page's admin log tail, giving crash incidents useful surrounding context instead of just an exit code.

Notable Bug Fixes

  • Embedded ports in pings: host strings like "localhost:5000" previously defaulted to https incorrectly and could conflict with an explicit port= argument. Mismatches now log a warning and are reconciled correctly.
  • IPv6 URL formatting: IPv6 literals like ::1 are now properly bracketed ([::1]:8080) in generated URLs.
  • Unified directory exclusions: if multiple routes serve files from the same directory, exclusions now merge globally, closing a gap where a secondary route could bypass another route's exclusions.

Behavioral Changes Worth Knowing About

  • /, /health, and /status are now served as implicit virtual default routes. Functionally unchanged, but get_all() and paths() now correctly list them instead of hiding them.
  • Bot subprocesses are now launched with piped output streams to support live log capture — meaning a bot checking sys.stdout.isatty() now sees False instead of the parent terminal's actual value. Worth checking if your bot branches on that.

Upgrading

pip install --upgrade "staypresent[prod]"
Enter fullscreen mode Exit fullscreen mode

Existing run() calls, web.* calls, and ping()/cron() usage all continue working unchanged — everything above is additive.

FAQs

Do I need to change any existing code to get the status page?
No — /status is served by default with no configuration required. web.status() is only needed for customization.

Does hang detection replace crash recovery, or add to it?
It adds to it — restart_on_crash still handles processes that actually exit; heartbeat_timeout catches the separate case of a process that's alive but stuck.

Is the admin log view secure by default?
A random API key is generated and logged if you don't set one yourself, and login attempts are rate-limited — reasonably safe by default, though setting your own key explicitly is recommended for anything public-facing.

Conclusion

StayPresent v1.6.0 turns the package from "keep-alive server plus process supervisor" into something closer to a full lightweight monitoring stack for bots — a real status page, hang detection for frozen processes, and deployment modes flexible enough to run the web server and the bot completely independently.

pip install "staypresent[prod]"
Enter fullscreen mode Exit fullscreen mode

Top comments (0)