Skip to content

Framework Guide

This page distills the conventions that every challenge server should follow. Use it as a checklist when bootstrapping a new challenges/<slug>/server app.

1. Bootstrap Observability

from ctf.common import configure_logging_and_metrics

metrics = configure_logging_and_metrics(ctf_slug="arkiv-beacon", service="server")
metrics.record_server_online(addr="0.0.0.0:8080")
  • Log lines should follow the readable logfmt-ish style described in the repo guidelines. Keep sensitive data out of logs.
  • The returned Metrics helper exposes convenience counters and histograms that can be safely called even when Prometheus is disabled.

2. Handle Arkiv Metadata Edge Cases

from ctf.common import get_entity_metadata_safe

metadata = await get_entity_metadata_safe(client, entity_key)

Use the helper when you need entity metadata during gameplay. It falls back to the raw RPC call if the higher-level SDK method temporarily fails so you can keep servicing requests without special retry logic.

3. React to Events, Don’t Poll

  • Use ArkivClient.watch_logs(...) (from the SDK) to subscribe to entity events.
  • Convert annotations to dictionaries with anns_to_dict to make filtering easier.
  • Never spin background loops-everything should be driven by WebSocket events.

4. Celebrate Completions

from ctf.common import asyncio_run_safely, send_discord_webhook

asyncio_run_safely(send_discord_webhook(challenge, team, winner_address))
  • The helper reads the webhook URL from DISCORD_WEBHOOK_URL so environments without a webhook stay silent.
  • Always run the call in the background; we never block the event loop on network I/O.

5. Document as You Go

When you add new helpers to ctf/common, include rich docstrings describing expected usage patterns. The API reference is generated at build time, so the documentation automatically stays current.